Step 0 set work directories

set.seed(2020)

Provide directories for training images. Training images and Training fiducial points will be in different subfolders.

train_dir <- "../data/train_set/" # This will be modified for different data sets.
train_image_dir <- paste(train_dir, "images/", sep="")
train_pt_dir <- paste(train_dir,  "points/", sep="")
train_label_path <- paste(train_dir, "label.csv", sep="")

Step 1: set up controls for evaluation experiments.

In this chunk, we have a set of controls for the evaluation experiments.

run.presentation.day <- FALSE #presentation day flag. No training. Generate a csv file
run.cv <- TRUE # run cross-validation on the training set
sample.reweight <- TRUE # run sample reweighting in model training
K <- 5  # number of CV folds
run.feature.train <- TRUE # process features for training set
run.test <- TRUE # run evaluation on an independent test set
run.feature.test <- TRUE # process features for test set

# gbm
run.gbm.train <- FALSE # gbm(imroved) is the chosen advanced model
run.gbm.test <- FALSE # gbm(imroved) is the chosen advanced model
gbm.numtrees <- 1000 #number of trees to use in gbm

#features options
run.poly.feature <- TRUE # process poly features
run.add.poly.feature <- TRUE # and poly features to features matrix

# svm
run.svm <- FALSE # svm is the chosen advanced model
model.selection <- FALSE # perform model selection on svm models
run.svm.test <- TRUE # evaluate performance on the test set

# Random Forest Model with new feature
run.rf <- FALSE
train.rf <- TRUE # Train Random Forest Model
test.rf <- TRUE # Test Random Forest Model

# Random Forest Model with old feature
run.rf.old.feature <- FALSE
train.rf.old.feature <- FALSE
test.rf.old.feature <- TRUE

# ridge
run.ridge <- FALSE # ridge is the chosen advanced model
alpha <- 0 # ridge regression
train.ridge <- TRUE # train ridge model

# PCA + LDA
run.pca_lda <- FALSE # PCA + LDA is the chosen adcanced model
run.select_PC <- TRUE #run different PCs
run.lda <- TRUE # run lda on the training set
run.pca_lad.test <- TRUE # evaluate performance on the test set

Using cross-validation or independent test set evaluation, we compare the performance of models with different specifications.

Step 2: import data and train-test split

#train-test split
info <- read.csv(train_label_path)
n <- nrow(info) #get number of rows from csv
n_train <- round(n*(4/5), 0) #use 4/5 amount of data for training
train_idx <- sample(info$Index, n_train, replace = F) #grab indexes used for training
test_idx <- setdiff(info$Index, train_idx) # get indexes not used for training

Fiducial points are stored in matlab format. In this step, we read them and store them in a list.

#function to read fiducial points
#input: index
#output: matrix of fiducial points corresponding to the index
n_files <- length(list.files(train_image_dir,'*jpg'))
readMat.matrix <- function(index){
     return(round(readMat(paste0(train_pt_dir, sprintf("%04d", index), ".mat"))[[1]],0))
}

if (run.presentation.day){
  test_idx <- c(1:n_files) #sample(n_files, n_files, replace = F)
  run.gbm.train <- FALSE
  run.feature.train <- FALSE
  run.gbm.test <- TRUE

  run.rf <- TRUE
  train.rf <- FALSE
  test.rf <- TRUE
}

#load fiducial points
fiducial_pt_list <- lapply(1:n_files, readMat.matrix)
save(fiducial_pt_list, file="../output/fiducial_pt_list.RData")

If on presentation day, we will run our baseline and advanced model, and produce a csv file containing label predictions.

Step 3: construct features and responses

Figure1

feature.R is the wrapper for all feature engineering functions and options. The function feature( ) have options that correspond to different scenarios for the project and produces an R object that contains features and responses that are required by all the models that are going to be evaluated later.

source("../lib/feature.R")
tm_feature_train <- NA
gbm_tm_feature_train <- NA
if(run.feature.train){
  tm_feature_train <- system.time(dat_train<-feature(fiducial_pt_list,train_idx, 
                                                     run.poly.feature, run.add.poly.feature))
  gbm_tm_feature_train <- system.time(gbm_dat_train<-feature(fiducial_pt_list,train_idx, 
                                                             FALSE, FALSE))
  save(gbm_dat_train, file="../output/gbm_feature_train.RData")
  save(dat_train, file="../output/feature_train.RData")
}else{
  #load(file="../output/feature_train.RData")
  #load(file="../output/gbm_feature_train.RData")
}

tm_feature_test <- NA
gbm_tm_feature_test <- NA
if(run.feature.test){
  tm_feature_test <- system.time(dat_test <- feature(fiducial_pt_list, test_idx, 
                                                     run.poly.feature, run.add.poly.feature))
  gbm_tm_feature_test <- system.time(gbm_dat_test <- feature(fiducial_pt_list, test_idx, 
                                                             FALSE, FALSE))
  save(gbm_dat_test, file="../output/gbm_feature_test.RData")
  save(dat_test, file="../output/feature_test.RData")
}else{
  load(file="../output/feature_test.RData")
  load(file="../output/gbm_feature_test.RData")
}

Step 4: train classification models with training features and responses; run test on test images

Call the train model and test model from library.

train.R and test.R are wrappers for all model training steps and classification/prediction steps.

source("../lib/train.R")
source("../lib/test.R")

Baseline GBM Model

if (run.gbm.train){
  if (sample.reweight){

    gbm_dat_train$label <- as.factor(gbm_dat_train$label)
    dat_train_balanced_gbm <- SMOTE(label ~ ., gbm_dat_train, perc.over = 100, perc.under=200)
    table(dat_train_balanced_gbm$label)

    gbm_tm_train <- system.time(gbm_train <- train_gbm(dat_train_balanced_gbm, s=0.1, 
                                                       K=K, n=gbm.numtrees,w = NULL))

  } else {
    gbm_tm_train <- system.time(gbm_train <- train_gbm(gbm_dat_train, s=0.1, 
                                                       K=K, n=gbm.numtrees,w = NULL))
  }

  # plot the performance
  best.iter.oob <- gbm.perf(gbm_train,method="OOB") # returns out-of-bag estimated best number of trees
  print(best.iter.oob)
  best.iter.cv <- gbm.perf(gbm_train,method="cv") # returns K-fold cv estimate of best number of trees
  print(best.iter.cv)

  saveRDS(gbm_train, "../output/gbm_model.rds")
  save(gbm_tm_train, best.iter.cv, file="../output/gbm_outputs.RData")
}
if(run.gbm.test){
  load(file="../output/gbm_outputs.RData")
  gbm_tm_test = NA
  feature_test <- as.matrix(gbm_dat_test[, 1:ncol(gbm_dat_test)-1])

  gbm_train <- readRDS("../output/gbm_model.rds")
  gbm_tm_test <- system.time(prob_pred_baseline<-test_gbm(gbm_train,as.data.frame(feature_test),
                                                          n=best.iter.cv,pred.type = 'response'))

  label_pred_baseline <- colnames(prob_pred_baseline)[apply(prob_pred_baseline, 1, which.max)]
}
if (run.gbm.test){
  load(file="../output/gbm_outputs.RData")
  gbm_accu <- mean(gbm_dat_test$label == label_pred_baseline)
  gbm.auc <- WeightedROC(as.numeric(label_pred_baseline), gbm_dat_test$label)
  gbm_auc = WeightedAUC(gbm.auc)
  cat("Time for constructing gbm training features=", gbm_tm_feature_train[1], "s \n")
  cat("Time for constructing gbm testing features=", gbm_tm_feature_test[1], "s \n")
  cat("The AUC of gbm model is", gbm_auc, ".\n")
  cat("The accuracy of GBM baseline model is", gbm_accu*100, "%.\n")
  cat("Time for training gbm model=", gbm_tm_train[1], "s \n")
  cat("Time for testing model=", gbm_tm_test[1], "s \n")
}

Advanced Model: Random Forest

The second advanced model is random forest. Here we use the datasets that are extracted by new feature functions. We used two models trained by both imbalanced and balanced dataset. We used SMOTE function to balance both training and testing data. For tuning the model, we set mtry = 308, tree number = 500, and node size = 10 for the RF model using balanced data (SMOTE), and we set mtry = 308, tree number = 1500, and node size = 30 for the RF model using imbalanced data. The evaluation of the model is shown at the end of this section.

The tuning part is in a separate file named “appendix_tune_rf.rmd” in doc folder. Please feel free to check that to see the tuning process.

We also trained random forest model with the datasets that are extracted by old feature functions. That is in part 6. Thank you for reading!

if(run.rf){
  ## Training RF
  if(train.rf){
    rf_dat_train <- dat_train
    rf_dat_train$label <- as.factor(rf_dat_train$label)
    dat_train_balanced_SMOTE <- SMOTE(label ~ ., rf_dat_train, perc.over = 120, perc.under=200)
    # Train RF by balanced data
    time.rf.train.final.balanced <- system.time(
      random_forest_fit_final_balanced <- random_forest_train(dat_train_balanced_SMOTE, 
                                                              mtry = 308, tree_number = 500, 
                                                              node_size = 10))
    save(random_forest_fit_final_balanced, file = "../output/rf_train_final_balanced.RData")
    save(time.rf.train.final.balanced, file = "../output/rf_train_final_time_balanced.RData")
    # Train RF by imbalanced data
    time.rf.train.final.imbalanced <- system.time(
      random_forest_fit_final_imbalanced <- random_forest_train(dat_train, 
                                                                mtry = 308, tree_number = 1500, 
                                                                node_size = 30))
    save(time.rf.train.final.imbalanced, file = "../output/rf_train_final_time_imbalanced.RData")
    save(random_forest_fit_final_imbalanced, file = "../output/rf_train_final_imbalanced.RData")
  }else{
    load("../output/rf_train_final_balanced.RData")
    load("../output/rf_train_final_time_balanced.RData")
    load("../output/rf_train_final_time_imbalanced.RData")
    load("../output/rf_train_final_imbalanced.RData")
  }
  # Evaluation:
  if(test.rf){
    rf_dat_test <- dat_test
    rf_dat_test$label <- as.numeric(rf_dat_test$label)
    
    time.rf.test.final.balanced <- system.time(
      rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_final_balanced,
                                                                       rf_dat_test))))
    rf_accuracy_balanced <- mean(round(rf_predicted_balanced == rf_dat_test$label))
    tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),rf_dat_test$label)
    rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
    
    cat("AUC for tuned Random Forest(balanced): ", rf_AUC_balanced,".\n")
    cat("Accuracy for tuned Random Forest(balanced)", rf_accuracy_balanced*100,"%.\n")
    cat("Training time for tuned Random Forest: ", time.rf.train.final.balanced[1], "s.\n")
    cat("Testing time for tuned Random Forest: ", time.rf.test.final.balanced[1], "s.\n")
    cat("   ","\n")
  }
}

We think RF model do not need Cross-Validation. Here is a snippet from Breiman’s official documentation: In random forests, there is no need for cross-validation or a separate test set to get an unbiased estimate of the test set error. It is estimated internally, during the run, as follows: Each tree is constructed using a different bootstrap sample from the original data. About one-third of the cases are left out of the bootstrap sample and not used in the construction of the kth tree. Put each case left out in the construction of the kth tree down the kth tree to get a classification. In this way, a test set classification is obtained for each case in about one-third of the trees. At the end of the run, take j to be the class that got most of the votes every time case n was oob. The proportion of times that j is not equal to the true class of n averaged over all cases is the oob error estimate. This has proven to be unbiased in many tests.

Alternative Model 1: SVM Model

If the given data set is imbalanced, we apply SMOTE to rebalance the data and use it to train our svm models.

if(run.svm){
  tm_svm_rebalanced_train <- NA
  if(sample.reweight){
    dat_train$label = as.factor(dat_train$label)
    tm_svm_rebalanced_train <- system.time(svm_training_data <- SMOTE(label ~ ., data = dat_train))
    save(tm_svm_rebalanced_train, file="../output/tm_svm_rebalanced_train.RData")
  } else {
    svm_training_data <- dat_train
    tm_svm_rebalanced_train <- tm_feature_train
  }
}

Tuning hyper-parameters for both linear and radial basis kernel and select the kernel method that produces the highest AUC and accuracy among the two methods.

if(run.svm){
  set.seed(2020)
  tm_svm_linear_mod <- NA
  tm_svm_radial_mod <- NA
  if(model.selection){
    svm_model_auc <- rep(NA, 2)
    svm_model_accu <- rep(NA, 2)
    ### linear kernel
    if(run.cv){
      best.linear.cost <- svm_linear_cost_tune(svm_training_data)
      cat("The best cost for svm model with linear kernel is: ", 
          best.linear.cost$best.parameters$cost)
      svm.linear.train.start = proc.time()
      tm_svm_linear_mod <- system.time(svm_linear_mod <- svm_linear_train(svm_training_data, 
                                                                          0.01, K))
      svm.linear.train.end = proc.time()
      svm.linear.tm = svm.linear.train.end - svm.linear.train.start
      save(svm_linear_mod, file="../output/svm_linear_mod.RData")
      save(svm.linear.tm[[3]], file="../output/tm_svm_linear_mod.RData")
    }
    svm_linear_pred <- svm_test(svm_linear_mod, svm_training_data, FALSE)
    # evaluate performance on linear kernel
    svm_model_accu[1] <- mean(round(svm_linear_pred == svm_training_data$label))
    tpr.fpr_linear <- WeightedROC(as.numeric(svm_linear_pred), svm_training_data$label)
    svm_model_auc[1] <- WeightedAUC(tpr.fpr_linear)

    #### radial basis kernel
    if(run.cv){
      #best.radial.cost <- svm_radial_cost_tune(svm_training_data)
      svm.rbf.train.start = proc.time()
      tm_svm_radial_mod < system.time(svm_radial_mod <- svm_radial_train(svm_training_data, 
                                                                         1, K))
      svm.rbf.train.end = proc.time()
      svm.rbf.tm = svm.rbf.train.end - svm.rbf.train.start
      save(svm_radial_mod, file="../output/svm_radial_mod.RData")
      save(svm.rbf.tm[[3]], file="../output/tm_svm_radial_mod.RData")
    }
    svm_radial_pred <- svm_test(svm_radial_mod, svm_training_data, FALSE)
    # evaluate performance on rbf kernel
    svm_model_accu[2] <- mean(round(svm_radial_pred == svm_training_data$label))
    tpr.fpr_default <- WeightedROC(as.numeric(svm_radial_pred), svm_training_data$label)
    svm_model_auc[2] <- WeightedAUC(tpr.fpr_default)

    # table to display results for the two kernel methods
    svm_res = matrix(rep(NA,6),ncol=3)
    svm_res[,1] = svm_model_accu
    svm_res[,2] = svm_model_auc
    svm_res[,3] = c(svm.linear.tm[3], svm.rbf.tm[3])
    colnames(svm_res) = c("Accuracy", "AUC","Running Time")
    rownames(svm_res) = c("linear","radial basis")
    save(svm_res, file="../output/svm_model_selection.RData")
  } else {
    if(!run.presentation.day){
      tm_svm_radial_mod < system.time(svm_radial_mod <- svm_radial_train(svm_training_data, 
                                                                         1, K))
    } else {
      load(file="../output/svm_radial_mod.RData")
      load(file="../output/svm_model_selection.RData")
      svm_res
    }
  }
}

Since radial basis kernel has higher accuracy and AUC than linear kernel, we will choose radial basis as our kernel method for training the svm model.

if(run.svm){
  svm_testing_data <- dat_test
  if(run.svm.test){
    ## rbf
    tm_svm_rbf_test <- system.time(svm_rbf_pred <- svm_test(svm_radial_mod, svm_testing_data, FALSE))
    svm_test_accu = mean(round(svm_rbf_pred == svm_testing_data$label))
    tpr.fpr.rbf <- WeightedROC(as.numeric(svm_rbf_pred), svm_testing_data$label)
    svm_test_auc = WeightedAUC(tpr.fpr.rbf)
    save(tm_svm_rbf_test, file="../output/tm_svm_rbf_test.RData")

    cat("The accuracy of svm model is", svm_test_accu*100, "%.\n")
    cat("The AUC of svm model is", svm_test_auc, ".\n")
  }
}
if(run.svm){
  cat("Time for construct training features =", tm_feature_train[1], "s \n")
  cat("Time for construct testing features =", tm_feature_test[1], "s \n")
  cat("Time for training svm model =", svm.rbf.tm[[3]], "s \n")
  cat("Time for testing svm model=", tm_svm_rbf_test[1], "s \n")
}

Alternative Model 2: Ridge Model

if(run.ridge){
  tm_ridge_train <- NA
  if (train.ridge){
    dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2020)$data
    tm_ridge_train <- system.time(ridge_cv_model<-ridge_train(train_data=dat_train_rebalanced, 
                                                              alpha=alpha, K=K, lambda=lambda))
    save(ridge_cv_model, file="../output/ridge_cv_model.RData")
    save(tm_ridge_train, file="../output/ridge_train_time.RData")
  } else {
    load(file="../output/ridge_cv_model.RData")
    load(file="../output/ridge_train_time.RData")
  }
}
if(run.ridge){
  if (run.cv){
    set.seed(2020)
    dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2020)$data
    feature_train = as.matrix(dat_train_rebalanced[,-dim(dat_train_rebalanced)[2]])
    label_train = as.integer(dat_train_rebalanced$label)
    ridge_model = cv.glmnet(x=feature_train, y=label_train, alpha=alpha, nfolds=K, lambda=lambda)
    opt_lambda = ridge_model$lambda.min
    save(opt_lambda, file="../output/ridge_optimal_lambda.RData")
  } else {
    load(file="../output/ridge_optimal_lambda.RData")
  }
}
if(run.ridge){
  tm_ridge_test = NA
  if(run.test){
    load("../output/ridge_cv_model.RData")
    dat_test_rebalanced <- ROSE(label ~ ., data = dat_test, seed=2020)$data
    feature_test <- as.matrix(dat_test_rebalanced[, -dim(dat_test_rebalanced)[2]])
    tm_ridge_test <- system.time(label_pred<-as.integer(ridge_test(model=ridge_cv_model, 
                                                                   features=feature_test, 
                                                                   pred.type = 'class')))
    save(tm_ridge_test, file="../output/ridge_test_time.RData")
  } else{
    load(file="../output/ridge_test_time.RData")
  }
}
if(run.ridge){
  cat("Time for constructing training features=", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
  cat("Time for training ridge model=", tm_ridge_train[1], "s \n")
  cat("Time for testing ridge model=", tm_ridge_test[1], "s \n")
}
if(run.ridge){
  load("../output/ridge_cv_model.RData")
  dat_test_rebalanced <- ROSE(label ~ ., data = dat_test, seed=2020)$data
  feature_test <- as.matrix(dat_test_rebalanced[, -dim(dat_test_rebalanced)[2]])
  label_pred = as.integer(predict(ridge_cv_model, s=opt_lambda, newx=feature_test, 
                                  type='class'))
  label_test = as.integer(dat_test_rebalanced$label)
  ridge_accuracy = mean(round(label_test== label_pred))
  cat("The accuracy of the ridge model is", ridge_accuracy*100, "%.\n")
  ridge_AUC = auc(roc(label_pred,label_test))
  cat("The AUC of the ridge model is", ridge_AUC, ".\n")
}

Alternative Model 3: PCA + LDA

```r
if(run.rf){
  source(\../lib/random_forest_old_feature.R\)
  if(tune.random.forest){
    # Train 500 trees:
    time.rf.train.tree500 <- system.time(random_forest_fit_500_trees <-
                                           random_forest_train_500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_500_trees, file = \../output/rf_train_500_trees.RData\)
    save(time.rf.train.tree500, file = \../output/rf_train_500_trees_time.RData\)
    cat(\500 tree time\, time.rf.train.tree500)
    # Train 1000 trees:
    time.rf.train.tree1000 <- system.time(random_forest_fit_1000_trees <-
                                            random_forest_train_1000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1000_trees, file = \../output/rf_train_1000_trees.RData\)
    save(time.rf.train.tree1000, file = \../output/rf_train_1000_trees_time.RData\)
    cat(\1000 tree time\, time.rf.train.tree1000)
    # Train 1500 trees:
    time.rf.train.tree1500 <- system.time(random_forest_fit_1500_trees <-
                                            random_forest_train_1500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_1500_trees, file = \../output/rf_train_1500_trees.RData\)
    save(time.rf.train.tree1500, file = \../output/rf_train_1500_trees_time.RData\)
    cat(\1500 tree time\, time.rf.train.tree1500)
    # Train 2000 trees:
    time.rf.train.tree2000 <- system.time(random_forest_fit_2000_trees <-
                                            random_forest_train_2000(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2000_trees, file = \../output/rf_train_2000_trees.RData\)
    save(time.rf.train.tree2000, file = \../output/rf_train_2000_trees_time.RData\)
    cat(\2000 tree time\, time.rf.train.tree2000)
    # Train 2500 trees:
    time.rf.train.tree2500 <- system.time(random_forest_fit_2500_trees <-
                                            random_forest_train_2500(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_2500_trees, file = \../output/rf_train_2500_trees.RData\)
    save(time.rf.train.tree2500, file = \../output/rf_train_2500_trees_time.RData\)
    cat(\2500 tree time\, time.rf.train.tree2500)

    # Train 10 nodes
    time.rf.train.node10 <- system.time(random_forest_fit_10_nodes <-
                                          random_forest_train_10(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_10_nodes, file = \../output/rf_train_10_nodes.RData\)
    save(time.rf.train.node10, file = \../output/rf_train_10_nodes_time.RData\)
    cat(\10 node time\, time.rf.train.node10)
    # Train 15 nodes
    time.rf.train.node15 <- system.time(random_forest_fit_15_nodes <-
                                          random_forest_train_15(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_15_nodes, file = \../output/rf_train_15_nodes.RData\)
    save(time.rf.train.node15, file = \../output/rf_train_15_nodes_time.RData\)
    cat(\15 node time\, time.rf.train.node15)
    # Train 20 nodes
    time.rf.train.node20 <- system.time(random_forest_fit_20_nodes <-
                                          random_forest_train_20(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_20_nodes, file = \../output/rf_train_20_nodes.RData\)
    save(time.rf.train.node20, file = \../output/rf_train_20_nodes_time.RData\)
    cat(\20 node time\, time.rf.train.node20)
    # Train 25 nodes
    time.rf.train.node25 <- system.time(random_forest_fit_25_nodes <-
                                          random_forest_train_25(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_25_nodes, file = \../output/rf_train_25_nodes.RData\)
    save(time.rf.train.node25, file = \../output/rf_train_25_nodes_time.RData\)
    cat(\25 node time\, time.rf.train.node25)
    # Train 30 nodes
    time.rf.train.node30 <- system.time(random_forest_fit_30_nodes <-
                                          random_forest_train_30(dat_train_balanced_rose,mtry = 308))
    save(random_forest_fit_30_nodes, file = \../output/rf_train_30_nodes.RData\)
    save(time.rf.train.node30, file = \../output/rf_train_30_nodes_time.RData\)
    cat(\30 node time\, time.rf.train.node30)
  } else {
    load(\../output/rf_train_500_trees.RData\)
    load(\../output/rf_train_1000_trees.RData\)
    load(\../output/rf_train_1500_trees.RData\)
    load(\../output/rf_train_2000_trees.RData\)
    load(\../output/rf_train_2500_trees.RData\)
    load(\../output/rf_train_10_nodes.RData\)
    load(\../output/rf_train_15_nodes.RData\)
    load(\../output/rf_train_20_nodes.RData\)
    load(\../output/rf_train_25_nodes.RData\)
    load(\../output/rf_train_30_nodes.RData\)
  }



  #Error rate of each hyperparameter:



  # Evaluate each hyperparameter


  # Predicted value from 500 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_500_trees, dat_test)))
  # Evaluate 500 trees:
  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test_balanced_rose$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 500\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 500\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 500\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 500\,rf_AUC_imbalanced,\.\n\)


  # Evaluation from 1000 trees' model:
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 1000\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 1000\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 1000\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 1000\,rf_AUC_imbalanced,\.\n\)

  # 1500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_1500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 1500\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 1500\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 1500\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 1500\,rf_AUC_imbalanced,\.\n\)

  #2000 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2000_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 2000\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 2000\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 2000\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 2000\,rf_AUC_imbalanced,\.\n\)

  # 2500 trees
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_2500_trees, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 2500\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 2500\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 2500\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 2500\,rf_AUC_imbalanced,\.\n\)

  # 10 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_10_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 10\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 10\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 10\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 10\,rf_AUC_imbalanced,\.\n\)

  # 15 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_15_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 15\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 15\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 15\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 15\,rf_AUC_imbalanced,\.\n\)

  # 20 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_20_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 20\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 20\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 20\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 20\,rf_AUC_imbalanced,\.\n\)

  # 25 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_25_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 25\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 25\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 25\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 25\,rf_AUC_imbalanced,\.\n\)

  # 30 nodes
  rf_predicted_balanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes,
                                                                   dat_test_balanced_rose)))
  rf_predicted_imbalanced <- as.numeric(as.vector(random_forest_test(random_forest_fit_30_nodes, dat_test)))

  rf_accuracy_balanced <- mean(round(rf_predicted_balanced == dat_test_balanced_rose$label))
  tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),dat_test$label)
  rf_AUC_balanced <- WeightedAUC(tpr.fpr.balanced)
  rf_accuracy_imbalanced <- mean(round(rf_predicted_imbalanced == dat_test_balanced_rose$label))
  tpr.fpr.imbalanced <- WeightedROC(as.numeric(rf_predicted_imbalanced),dat_test$label)
  rf_AUC_imbalanced <- WeightedAUC(tpr.fpr.imbalanced)

  cat(\Accuracy(balanced) 30\, rf_accuracy_balanced*100,\%.\n\)
  cat(\AUC(balanced) 30\, rf_AUC_balanced,\.\n\)
  cat(\Accuracy(imbalanced) 30\, rf_accuracy_imbalanced*100,\%.\n\)
  cat(\AUC(imbalanced) 30\,rf_AUC_imbalanced,\.\n\)
}

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->


* Perform PCA for Dimension Reduction

**Since there are over 6000 features, we implement the PCA method to reduce dimension according to the covariance matrix. We only retain PCs with large variance.**


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuaWYocnVuLnBjYV9sZGEpe1xuICBiYWxhbmNlZF90ZXN0X2RhdGEgPC0gZGF0X3Rlc3RcbiAgaWYocnVuLnNlbGVjdF9QQyl7XG4gICAgI3NlcGFyYXRlIHRoZSBmZWF0dXJlcyBmcm9tIGxhYmVsXG4gICAgZGF0X3RyYWluX25ldyA8LSBiYWxhbmNlZF90cmFpbl9kYXRhWywtZGltKGJhbGFuY2VkX3RyYWluX2RhdGEpWzJdXVxuICAgIGRhdF90ZXN0X25ldyA8LSBiYWxhbmNlZF90ZXN0X2RhdGFbLC1kaW0oYmFsYW5jZWRfdGVzdF9kYXRhKVsyXV1cbiAgICAjY3JlYXRlIGEgdmVjdG9yIGNvbnRhaW4gdGFyZ2V0IG51bWJlciBvZiBQQ3NcbiAgICBudW0ucGNhIDwtIGMoMTAsNTAsNTAwLDEwMDApXG4gICAgdHJhaW5fcGNhIDwtIGZ1bmN0aW9uKG51bS5wY2Epe1xuICAgICAgZm9yKGkgaW4gMTpsZW5ndGgobnVtLnBjYSkpe1xuICAgICAgICAjc3RhcnQgdGltZSBmb3IgdHJhaW5pbmcgdGhlIG1vZGVsXG4gICAgICAgIHRyYWluLm1vZGVsLnN0YXJ0ID0gcHJvYy50aW1lKClcbiAgICAgICAgI3J1biBQQ0FcbiAgICAgICAgcGNhIDwtIHByY29tcChkYXRfdHJhaW5fbmV3KVxuICAgICAgICAjc3RvcmUgZm9yIGVhY2ggcG90ZW50aWFsIFBDXG4gICAgICAgIHRyYWluX3BjYSA8LSBkYXRhLmZyYW1lKHBjYSR4WywxOm51bS5wY2FbaV1dLCBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBiYWxhbmNlZF90cmFpbl9kYXRhW2RpbShiYWxhbmNlZF90cmFpbl9kYXRhKVsyXV0pXG4gICAgICAgIHByZWRfcGNhIDwtIHByZWRpY3QocGNhLGRhdF90ZXN0X25ldylcbiAgICAgICAgdGVzdF9wY2EgPC0gZGF0YS5mcmFtZShwcmVkX3BjYVssMTpudW0ucGNhW2ldXSwgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBiYWxhbmNlZF90ZXN0X2RhdGFbZGltKGJhbGFuY2VkX3Rlc3RfZGF0YSlbMl1dKVxuICAgICAgICAjZml0dGluZyB0aGUgbGRhIG1vZGVsXG4gICAgICAgIGxkYV9wY2EgPC0gbGRhKGxhYmVsIH4gLiwgZGF0YSA9IHRyYWluX3BjYSlcbiAgICAgICAgI3N0b3AgdGltZSBmb3IgdHJhaW5pbmcgdGhlIG1vZGVsXG4gICAgICAgIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpXG4gICAgICAgICNzdGFydCB0aW1lIGZvciB0ZXN0aW5nIHRoZSBtb2RlbFxuICAgICAgICB0ZXN0Lm1vZGVsLnN0YXJ0ID0gcHJvYy50aW1lKClcbiAgICAgICAgI3ByZWRpY3QgbGRhIG1vZGVsXG4gICAgICAgIGxkYV9wcmVkX3BjYSA9IHByZWRpY3QobGRhX3BjYSx0ZXN0X3BjYVstZGltKHRlc3RfcGNhKVsyXV0pXG4gICAgICAgICNlbmQgdGltZSBmb3IgdGVzdGluZyB0aGUgbW9kZWxcbiAgICAgICAgdGVzdC5tb2RlbC5lbmQgPSBwcm9jLnRpbWUoKVxuICAgICAgICAjdGVzdCBhY2N1cmFjeVxuICAgICAgICB0ZXN0X2FjY3VyYWN5PWNvbmZ1c2lvbk1hdHJpeChsZGFfcHJlZF9wY2EkY2xhc3MsIHRlc3RfcGNhJGxhYmVsKSRvdmVyYWxsWzFdXG4gICAgICAgIHByaW50KGxpc3QobDE9dHJhaW4ubW9kZWwuZW5kIC0gdHJhaW4ubW9kZWwuc3RhcnQsXG4gICAgICAgICAgICAgICAgICAgbDI9dGVzdC5tb2RlbC5lbmQgLSB0ZXN0Lm1vZGVsLnN0YXJ0LFxuICAgICAgICAgICAgICAgICAgIGwzPXRlc3RfYWNjdXJhY3kpKVxuICAgICAgfVxuICAgIH1cbiAgICB0cmFpbl9wY2EobnVtLnBjYSlcbiAgfVxufVxuYGBgIn0= -->

```r
if(run.pca_lda){
  balanced_test_data <- dat_test
  if(run.select_PC){
    #separate the features from label
    dat_train_new <- balanced_train_data[,-dim(balanced_train_data)[2]]
    dat_test_new <- balanced_test_data[,-dim(balanced_test_data)[2]]
    #create a vector contain target number of PCs
    num.pca <- c(10,50,500,1000)
    train_pca <- function(num.pca){
      for(i in 1:length(num.pca)){
        #start time for training the model
        train.model.start = proc.time()
        #run PCA
        pca <- prcomp(dat_train_new)
        #store for each potential PC
        train_pca <- data.frame(pca$x[,1:num.pca[i]], 
                                label = balanced_train_data[dim(balanced_train_data)[2]])
        pred_pca <- predict(pca,dat_test_new)
        test_pca <- data.frame(pred_pca[,1:num.pca[i]], 
                               label = balanced_test_data[dim(balanced_test_data)[2]])
        #fitting the lda model
        lda_pca <- lda(label ~ ., data = train_pca)
        #stop time for training the model
        train.model.end = proc.time()
        #start time for testing the model
        test.model.start = proc.time()
        #predict lda model
        lda_pred_pca = predict(lda_pca,test_pca[-dim(test_pca)[2]])
        #end time for testing the model
        test.model.end = proc.time()
        #test accuracy
        test_accuracy=confusionMatrix(lda_pred_pca$class, test_pca$label)$overall[1]
        print(list(l1=train.model.end - train.model.start,
                   l2=test.model.end - test.model.start,
                   l3=test_accuracy))
      }
    }
    train_pca(num.pca)
  }
}

By comparing the training time, test time and accuracy, we use model with 500 PCs.

if(run.pca_lda){
  train.model.start = proc.time()
  if(run.lda.train){
    pca_500 <- prcomp(balanced_train_data[,-dim(balanced_train_data)[2]])
    train_pca_500 <- data.frame(pca_500$x[,1:500], 
                                label = balanced_train_data[dim(balanced_train_data)[2]])
    pred_pca_500 <- predict(pca_500,balanced_test_data[,-dim(balanced_test_data)[2]])
    test_pca_500 <- data.frame(pred_pca_500[,1:500], 
                               label = balanced_test_data[dim(balanced_test_data)[2]])
    save(train_pca_500, file="../output/feature_pca_train.RData")
    save(test_pca_500, file="../output/feature_pca_test.RData")  
  } else {
    load(train_pca_50, file="../output/feature_pca_train.RData")
    load(test_pca_50, file="../output/feature_pca_test.RData")  
  }
  #calculate the training time
  lda_pca_50 <- lda(label ~ ., data = train_pca_50, cv = TRUE)
  train.model.end = proc.time()
}
if(run.pca_lda){
  test.model.start = proc.time()
  pred_train_lda <- predict(lda_pca_500, train_pca_500[-dim(train_pca_500)[2]])
  accu_train_lda <- mean(pred_train_lda$class == train_pca_500$label)
  cat("The trainig accuracy of model: LDA", "is", accu_train_lda*100, "%.\n")
  #calculating the test time
  if(run.test){
    pred_test_lda <- predict(lda_pca_500, test_pca_500)
  }
  test.model.end = proc.time()
  save(pred_test_lda, file="../output/fit_train.RData")
  accu_test_lda <- mean(pred_test_lda$class == test_pca_500$label)
  cat("The accuracy of model: LDA", "is", accu_test_lda*100, "%.\n")
  tpr.fpr <- WeightedROC(as.numeric(pred_test_lda$class), test_pca_500$label)
  lda_auc = WeightedAUC(tpr.fpr)
  cat("The AUC of model: LDA is", lda_auc, ".\n")
}

Prediction performance matters, so does the running times for constructing features and for training the model, especially when the computation resource is limited.

if(run.pca_lda){
  tm_train <- train.model.end - train.model.start
  tm_test <- test.model.end - test.model.start
  cat("Time for constructing training features =", tm_feature_train[1], "s \n")
  cat("Time for constructing testing features =", tm_feature_test[1], "s \n")
  cat("Time for training model =", tm_train[1], "s \n")
  cat("Time for testing model =", tm_test[1], "s \n")
}

Alternative Model 4: Random Forest (RF) Model with old features:

The fourth alternative model is random forest using old features given in the starter code. Here we use the datasets that are extracted by old feature functions. We used two models trained by both imbalanced and balanced dataset. We used ROSE function to balance both training and testing data. For tuning the model, we set mtry = 308, tree number = 500, and node size = 10 for the RF model using balanced data, and we set mtry = 308, tree number = 1500, and node size = 30 for the RF model using imbalanced data. The evaluation of the model is shown at the end of this section, and we will compare this model with the RF model trained by new features.

if(run.rf.old.feature){
  old_feature <- function(input_list = fiducial_pt_list, index){
    old_pairwise_dist <- function(vec){
      return(as.vector(dist(vec)))
    }
    old_pairwise_dist_result <-function(mat){
      return(as.vector(apply(mat, 2, old_pairwise_dist)))
    }
    old_pairwise_dist_feature <- t(sapply(input_list[index], old_pairwise_dist_result))
    dim(old_pairwise_dist_feature)
    old_pairwise_data <- cbind(old_pairwise_dist_feature, info$label[index])
    colnames(old_pairwise_data) <- c(paste("feature", 1:(ncol(old_pairwise_data)-1), sep = ""), 
                                     "label")
    old_pairwise_data <- as.data.frame(old_pairwise_data)
    old_pairwise_data$label <- as.factor(old_pairwise_data$label)
    return(feature_df = old_pairwise_data)
  }

  old_tm_feature_train <- NA
  if(run.feature.train){
    old_tm_feature_train <- system.time(old_dat_train <- old_feature(fiducial_pt_list, train_idx))
    save(old_dat_train, file="../output/feature_train_old.RData")
  }else{
    load(file="../output/feature_train_old.RData")
  }
  old_tm_feature_test <- NA
  if(run.feature.test){
    old_tm_feature_test <- system.time(old_dat_test <- old_feature(fiducial_pt_list, test_idx))
    save(old_dat_test, file="../output/feature_test_old.RData")
  }else{
    load(file="../output/feature_test_old.RData")
  }
  # transfer label column from factor to numeric
  old_dat_train$label <- as.numeric(old_dat_train$label)
  old_dat_test$label <- as.numeric(old_dat_test$label)
  
  # Balance data
  gbm_dat_train$label <- as.factor(gbm_dat_train$label)
  dat_train_balanced_gbm <- SMOTE(label ~ ., gbm_dat_train, perc.over = 100, perc.under=200)
  old_dat_train_balanced_SMOTE <- dat_train_balanced_gbm

  # Train Model
  if(train.rf.old.feature){
    # Balanced
    old_time.rf.train.final.balanced <- system.time(
      old_random_forest_fit_final_balanced <- old_random_forest_train(old_dat_train_balanced_SMOTE, 
                                                                      mtry = 308, tree_number = 500, 
                                                                      node_size = 10))
    save(old_random_forest_fit_final_balanced, 
         file = "../output/rf_train_final_balanced_old_feature.RData")
    save(old_time.rf.train.final.balanced, 
         file = "../output/rf_train_final_time_balanced_old_feature.RData")
    # Imbalanced
    old_time.rf.train.final.imbalanced <- system.time(
      old_random_forest_fit_final_imbalanced <- old_random_forest_train(old_dat_train, 
                                                                        mtry = 308, 
                                                                        tree_number = 1500, 
                                                                        node_size = 30))
    save(old_time.rf.train.final.imbalanced, 
         file = "../output/rf_train_final_time_imbalanced_old_feature.RData")
    save(old_random_forest_fit_final_imbalanced, 
         file = "../output/rf_train_final_imbalanced_old_feature.RData")
  }else{
    load("../output/rf_train_final_balanced_old_feature.RData")
    load("../output/rf_train_final_time_balanced_old_feature.RData")
    load("../output/rf_train_final_time_imbalanced_old_feature.RData")
    load("../output/rf_train_final_imbalanced_old_feature.RData")
  }


  # Evaluate Model
  old_rf_dat_test <- old_dat_test
  old_rf_dat_test$label <- as.numeric(old_rf_dat_test$label)
  if(test.rf.old.feature){
  old_time.rf.test.final.balanced <- system.time(
    rf_predicted_balanced <- as.numeric(as.vector(old_random_forest_test(old_random_forest_fit_final_balanced, old_rf_dat_test))))
  old_rf_accuracy_balanced <- mean(round(rf_predicted_balanced == old_rf_dat_test$label))
  old_tpr.fpr.balanced <- WeightedROC(as.numeric(rf_predicted_balanced),old_rf_dat_test$label)
  old_rf_AUC_balanced <- WeightedAUC(old_tpr.fpr.balanced)
  
  cat("AUC(balanced) for Random Forest with old feature: ", old_rf_AUC_balanced,".\n")
  cat("Accuracy(balanced) for Random Forest with old feature", old_rf_accuracy_balanced*100,"%.\n")
  cat("Training time (balanced) for Random Forest with old feature: ", old_time.rf.train.final.balanced[1], "s.\n")
  cat("Testing time (balanced) for Random Forest with old feature: ", old_time.rf.test.final.balanced[1], "s.\n")
  cat("   ","\n")
  }
}

Generate a csv on presentation day

if (run.presentation.day){
  csvfileoutput<-"../output/label_prediction.csv"
  Advanced<-rf_predicted_balanced
  Baseline<-label_pred_baseline
  Index<-test_idx
  csvdata <- data.frame(Index, Baseline, Advanced)

  write.csv(csvdata,csvfileoutput, row.names=FALSE,quote = FALSE)
}

Reference

  • Du, S., Tao, Y., & Martinez, A. M. (2014). Compound facial expressions of emotion. Proceedings of the National Academy of Sciences, 111(15), E1454-E1462.
LS0tCnRpdGxlOiAiUHJvamVjdCAzOiBGYWNpYWwgRXhwcmVzc2lvbiBQcmVkaWN0aXZlIE1vZGVsaW5nIgpzdWJ0aXRsZTogIkdyb3VwIDUiCmF1dGhvcjogIkppbmdiaW4gQ2FvLCBDaHVhbmNodWFuIExpdSwgRGVubmlzIFNocGl0cywgWWluZ3lhbyBXdSwgWmlrdW4gWmh1YW5nIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojVGVzdCBCcmFuY2ggY3JlYXRlZAppZighcmVxdWlyZSgiUi5tYXRsYWIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiUi5tYXRsYWIiKQp9CmlmKCFyZXF1aXJlKCJyZWFkeGwiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKfQppZighcmVxdWlyZSgiZHBseXIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQp9CmlmKCFyZXF1aXJlKCJyZWFkeGwiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKfQppZighcmVxdWlyZSgiZ2dwbG90MiIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKfQppZighcmVxdWlyZSgiY2FyZXQiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQp9CmlmKCFyZXF1aXJlKCJnbG1uZXQiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikKfQppZighcmVxdWlyZSgiV2VpZ2h0ZWRST0MiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiV2VpZ2h0ZWRST0MiKQp9CmlmKCFyZXF1aXJlKCJnYm0iKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2JtIikKfQppZighcmVxdWlyZSgiRE13UiIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJETXdSIikKfQppZighcmVxdWlyZSgiT3BlbkltYWdlUiIpKXsKIGluc3RhbGwucGFja2FnZXMoIk9wZW5JbWFnZVIiKQp9CmlmKCFyZXF1aXJlKCJBVUMiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJBVUMiKQp9CmlmKCFyZXF1aXJlKCJlMTA3MSIpKXsKIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikKfQppZighcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpewogaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKfQppZighcmVxdWlyZSgieGdib29zdCIpKXsKIGluc3RhbGwucGFja2FnZXMoInhnYm9vc3QiKQp9CmlmKCFyZXF1aXJlKCJ0aWJibGUiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ0aWJibGUiKQp9CmlmKCFyZXF1aXJlKCJST1NFIikpewogaW5zdGFsbC5wYWNrYWdlcygiUk9TRSIpCn0KaWYoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKXsKIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCn0KaWYoIXJlcXVpcmUoImNhVG9vbHMiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiY2FUb29scyIpCn0KaWYoIXJlcXVpcmUoInByZWRpY3Rpb24iKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicHJlZGljdGlvbiIpCn0KaWYoIXJlcXVpcmUoInBST0MiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpCn0KaWYoIXJlcXVpcmUoIkRNd1IiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiRE13UiIpCn0KaWYoIXJlcXVpcmUoIk1BU1MiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpCn0KbGlicmFyeShSLm1hdGxhYikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjYXJldCkKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkoV2VpZ2h0ZWRST0MpCmxpYnJhcnkoZ2JtKQpsaWJyYXJ5KERNd1IpCiMjIyBuZXcgbGlicmFyaWVzCmxpYnJhcnkoT3BlbkltYWdlUikKbGlicmFyeShBVUMpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHhnYm9vc3QpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KFJPU0UpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNhVG9vbHMpCmxpYnJhcnkocHJlZGljdGlvbikKbGlicmFyeShwUk9DKQpsaWJyYXJ5KE1BU1MpCmBgYAoKCiMjIyBTdGVwIDAgc2V0IHdvcmsgZGlyZWN0b3JpZXMKYGBge3Igd2tkaXIsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDIwMjApCmBgYAoKUHJvdmlkZSBkaXJlY3RvcmllcyBmb3IgdHJhaW5pbmcgaW1hZ2VzLiBUcmFpbmluZyBpbWFnZXMgYW5kIFRyYWluaW5nIGZpZHVjaWFsIHBvaW50cyB3aWxsIGJlIGluIGRpZmZlcmVudCBzdWJmb2xkZXJzLgoKYGBge3J9CnRyYWluX2RpciA8LSAiLi4vZGF0YS90cmFpbl9zZXQvIiAjIFRoaXMgd2lsbCBiZSBtb2RpZmllZCBmb3IgZGlmZmVyZW50IGRhdGEgc2V0cy4KdHJhaW5faW1hZ2VfZGlyIDwtIHBhc3RlKHRyYWluX2RpciwgImltYWdlcy8iLCBzZXA9IiIpCnRyYWluX3B0X2RpciA8LSBwYXN0ZSh0cmFpbl9kaXIsICAicG9pbnRzLyIsIHNlcD0iIikKdHJhaW5fbGFiZWxfcGF0aCA8LSBwYXN0ZSh0cmFpbl9kaXIsICJsYWJlbC5jc3YiLCBzZXA9IiIpCmBgYAoKIyMjIFN0ZXAgMTogc2V0IHVwIGNvbnRyb2xzIGZvciBldmFsdWF0aW9uIGV4cGVyaW1lbnRzLgoKSW4gdGhpcyBjaHVuaywgd2UgaGF2ZSBhIHNldCBvZiBjb250cm9scyBmb3IgdGhlIGV2YWx1YXRpb24gZXhwZXJpbWVudHMuCgorIChUL0YpIHdoZXRoZXIgaXQgaXMgb24gcHJlc2VudGF0aW9uIGRheSBmb3IgcHJvZHVjaW5nIHRoZSBjc3YgZmlsZQorIChUL0YpIGNyb3NzLXZhbGlkYXRpb24gb24gdGhlIHRyYWluaW5nIHNldAorIChUL0YpIHJld2VpZ2h0aW5nIHRoZSBzYW1wbGVzIGZvciB0cmFpbmluZyBzZXQKKyAobnVtYmVyKSBLLCB0aGUgbnVtYmVyIG9mIENWIGZvbGRzCisgKFQvRikgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CisgKFQvRikgcnVuIGV2YWx1YXRpb24gb24gYW4gaW5kZXBlbmRlbnQgdGVzdCBzZXQKKyAoVC9GKSBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKKyAoVC9GKSBydW4gZ2JtIGJhc2VsaW5lIG1vZGVsCisgKFQvRikgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CisgKG51bWJlcikgZ2JtLm51bXRyZWVzLCB0aGUgbnVtYmVyIG9mIHRyZWVzIHRvIHVzZSBpbiBHQk0gYmFzZWxpbmUKCisgKFQvRikgcmV0dXJuIHBvbHlub21pYWwgZmVhdHVyZXMgbWF0cml4IG9ubHkKKyAoVC9GKSBhZGQgcG9seW5vbWlhbCBmZWF0dXJlcyB0byBzdGFydGVyIGNvZGUgZmVhdHVyZXMgbWF0cml4CgorIChUL0YpIHJ1biBzdm0gbW9kZWwKKyAoVC9GKSBwZXJmb3JtIG1vZGVsIHNlbGVjdGlvbiBvdmVyIGEgbGlzdCBvZiBzdm0gbW9kZWxzCisgKFQvRikgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CgorIChUL0YpIHJ1biByYW5kb20gZm9yZXN0IG1vZGVsCisgKFQvRikgdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbAorIChUL0YpIHJ1biBldmFsdWF0aW9uIG9uIHRoZSB0ZXN0IHNldAoKKyAoVC9GKSBydW4gcmlkZ2UgbW9kZWwKKyAoMC8xKSBhbHBoYSwgYWxwaGE9MCBmb3IgcmlkZ2UgcmVncmVzc2lvbiwgYWxwaGE9MSBmb3IgbGFzc28gcmVncmVzc2lvbgorIChUL0YpIHRyYWluIHJpZGdlIG1vZGVsCgorIChUL0YpIHJ1biBQQ0ErTERBIG1vZGVsCisgKFQvRikgcnVuIGRpZmZlcmVudCBwcmluY2lwYWwgY29tcG9uZW50cworIChUL0YpIHJ1biBMREEgb24gdHJhaW5pbmcgc2V0CisgKFQvRikgcnVuIGV2YWx1YXRpb24gb24gdGhlIHRlc3Qgc2V0CgpgYGB7ciBleHBfc2V0dXB9CnJ1bi5wcmVzZW50YXRpb24uZGF5IDwtIEZBTFNFICNwcmVzZW50YXRpb24gZGF5IGZsYWcuIE5vIHRyYWluaW5nLiBHZW5lcmF0ZSBhIGNzdiBmaWxlCnJ1bi5jdiA8LSBUUlVFICMgcnVuIGNyb3NzLXZhbGlkYXRpb24gb24gdGhlIHRyYWluaW5nIHNldApzYW1wbGUucmV3ZWlnaHQgPC0gVFJVRSAjIHJ1biBzYW1wbGUgcmV3ZWlnaHRpbmcgaW4gbW9kZWwgdHJhaW5pbmcKSyA8LSA1ICAjIG51bWJlciBvZiBDViBmb2xkcwpydW4uZmVhdHVyZS50cmFpbiA8LSBUUlVFICMgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CnJ1bi50ZXN0IDwtIFRSVUUgIyBydW4gZXZhbHVhdGlvbiBvbiBhbiBpbmRlcGVuZGVudCB0ZXN0IHNldApydW4uZmVhdHVyZS50ZXN0IDwtIFRSVUUgIyBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKIyBnYm0KcnVuLmdibS50cmFpbiA8LSBGQUxTRSAjIGdibShpbXJvdmVkKSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCnJ1bi5nYm0udGVzdCA8LSBGQUxTRSAjIGdibShpbXJvdmVkKSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCmdibS5udW10cmVlcyA8LSAxMDAwICNudW1iZXIgb2YgdHJlZXMgdG8gdXNlIGluIGdibQoKI2ZlYXR1cmVzIG9wdGlvbnMKcnVuLnBvbHkuZmVhdHVyZSA8LSBUUlVFICMgcHJvY2VzcyBwb2x5IGZlYXR1cmVzCnJ1bi5hZGQucG9seS5mZWF0dXJlIDwtIFRSVUUgIyBhbmQgcG9seSBmZWF0dXJlcyB0byBmZWF0dXJlcyBtYXRyaXgKCiMgc3ZtCnJ1bi5zdm0gPC0gRkFMU0UgIyBzdm0gaXMgdGhlIGNob3NlbiBhZHZhbmNlZCBtb2RlbAptb2RlbC5zZWxlY3Rpb24gPC0gRkFMU0UgIyBwZXJmb3JtIG1vZGVsIHNlbGVjdGlvbiBvbiBzdm0gbW9kZWxzCnJ1bi5zdm0udGVzdCA8LSBUUlVFICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CgojIFJhbmRvbSBGb3Jlc3QgTW9kZWwgd2l0aCBuZXcgZmVhdHVyZQpydW4ucmYgPC0gRkFMU0UKdHJhaW4ucmYgPC0gVFJVRSAjIFRyYWluIFJhbmRvbSBGb3Jlc3QgTW9kZWwKdGVzdC5yZiA8LSBUUlVFICMgVGVzdCBSYW5kb20gRm9yZXN0IE1vZGVsCgojIFJhbmRvbSBGb3Jlc3QgTW9kZWwgd2l0aCBvbGQgZmVhdHVyZQpydW4ucmYub2xkLmZlYXR1cmUgPC0gRkFMU0UKdHJhaW4ucmYub2xkLmZlYXR1cmUgPC0gRkFMU0UKdGVzdC5yZi5vbGQuZmVhdHVyZSA8LSBUUlVFCgojIHJpZGdlCnJ1bi5yaWRnZSA8LSBGQUxTRSAjIHJpZGdlIGlzIHRoZSBjaG9zZW4gYWR2YW5jZWQgbW9kZWwKYWxwaGEgPC0gMCAjIHJpZGdlIHJlZ3Jlc3Npb24KdHJhaW4ucmlkZ2UgPC0gVFJVRSAjIHRyYWluIHJpZGdlIG1vZGVsCgojIFBDQSArIExEQQpydW4ucGNhX2xkYSA8LSBGQUxTRSAjIFBDQSArIExEQSBpcyB0aGUgY2hvc2VuIGFkY2FuY2VkIG1vZGVsCnJ1bi5zZWxlY3RfUEMgPC0gVFJVRSAjcnVuIGRpZmZlcmVudCBQQ3MKcnVuLmxkYSA8LSBUUlVFICMgcnVuIGxkYSBvbiB0aGUgdHJhaW5pbmcgc2V0CnJ1bi5wY2FfbGFkLnRlc3QgPC0gVFJVRSAjIGV2YWx1YXRlIHBlcmZvcm1hbmNlIG9uIHRoZSB0ZXN0IHNldApgYGAKClVzaW5nIGNyb3NzLXZhbGlkYXRpb24gb3IgaW5kZXBlbmRlbnQgdGVzdCBzZXQgZXZhbHVhdGlvbiwgd2UgY29tcGFyZSB0aGUgcGVyZm9ybWFuY2Ugb2YgbW9kZWxzIHdpdGggZGlmZmVyZW50IHNwZWNpZmljYXRpb25zLgoKIyMjIFN0ZXAgMjogaW1wb3J0IGRhdGEgYW5kIHRyYWluLXRlc3Qgc3BsaXQKYGBge3J9CiN0cmFpbi10ZXN0IHNwbGl0CmluZm8gPC0gcmVhZC5jc3YodHJhaW5fbGFiZWxfcGF0aCkKbiA8LSBucm93KGluZm8pICNnZXQgbnVtYmVyIG9mIHJvd3MgZnJvbSBjc3YKbl90cmFpbiA8LSByb3VuZChuKig0LzUpLCAwKSAjdXNlIDQvNSBhbW91bnQgb2YgZGF0YSBmb3IgdHJhaW5pbmcKdHJhaW5faWR4IDwtIHNhbXBsZShpbmZvJEluZGV4LCBuX3RyYWluLCByZXBsYWNlID0gRikgI2dyYWIgaW5kZXhlcyB1c2VkIGZvciB0cmFpbmluZwp0ZXN0X2lkeCA8LSBzZXRkaWZmKGluZm8kSW5kZXgsIHRyYWluX2lkeCkgIyBnZXQgaW5kZXhlcyBub3QgdXNlZCBmb3IgdHJhaW5pbmcKYGBgCgpGaWR1Y2lhbCBwb2ludHMgYXJlIHN0b3JlZCBpbiBtYXRsYWIgZm9ybWF0LiBJbiB0aGlzIHN0ZXAsIHdlIHJlYWQgdGhlbSBhbmQgc3RvcmUgdGhlbSBpbiBhIGxpc3QuCgpgYGB7ciByZWFkIGZpZHVjaWFsIHBvaW50c30KI2Z1bmN0aW9uIHRvIHJlYWQgZmlkdWNpYWwgcG9pbnRzCiNpbnB1dDogaW5kZXgKI291dHB1dDogbWF0cml4IG9mIGZpZHVjaWFsIHBvaW50cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBpbmRleApuX2ZpbGVzIDwtIGxlbmd0aChsaXN0LmZpbGVzKHRyYWluX2ltYWdlX2RpciwnKmpwZycpKQpyZWFkTWF0Lm1hdHJpeCA8LSBmdW5jdGlvbihpbmRleCl7CiAgICAgcmV0dXJuKHJvdW5kKHJlYWRNYXQocGFzdGUwKHRyYWluX3B0X2Rpciwgc3ByaW50ZigiJTA0ZCIsIGluZGV4KSwgIi5tYXQiKSlbWzFdXSwwKSkKfQoKaWYgKHJ1bi5wcmVzZW50YXRpb24uZGF5KXsKICB0ZXN0X2lkeCA8LSBjKDE6bl9maWxlcykgI3NhbXBsZShuX2ZpbGVzLCBuX2ZpbGVzLCByZXBsYWNlID0gRikKICBydW4uZ2JtLnRyYWluIDwtIEZBTFNFCiAgcnVuLmZlYXR1cmUudHJhaW4gPC0gRkFMU0UKICBydW4uZ2JtLnRlc3QgPC0gVFJVRQoKICBydW4ucmYgPC0gVFJVRQogIHRyYWluLnJmIDwtIEZBTFNFCiAgdGVzdC5yZiA8LSBUUlVFCn0KCiNsb2FkIGZpZHVjaWFsIHBvaW50cwpmaWR1Y2lhbF9wdF9saXN0IDwtIGxhcHBseSgxOm5fZmlsZXMsIHJlYWRNYXQubWF0cml4KQpzYXZlKGZpZHVjaWFsX3B0X2xpc3QsIGZpbGU9Ii4uL291dHB1dC9maWR1Y2lhbF9wdF9saXN0LlJEYXRhIikKYGBgCgpJZiBvbiBwcmVzZW50YXRpb24gZGF5LCB3ZSB3aWxsIHJ1biBvdXIgYmFzZWxpbmUgYW5kIGFkdmFuY2VkIG1vZGVsLCBhbmQgcHJvZHVjZSBhIGNzdiBmaWxlIGNvbnRhaW5pbmcgbGFiZWwgcHJlZGljdGlvbnMuCgojIyMgU3RlcCAzOiBjb25zdHJ1Y3QgZmVhdHVyZXMgYW5kIHJlc3BvbnNlcwoKKyBUaGUgZm9sbG93IHBsb3RzIHNob3cgaG93IHBhaXJ3aXNlIGRpc3RhbmNlIGJldHdlZW4gZmlkdWNpYWwgcG9pbnRzIGNhbiB3b3JrIGFzIGZlYXR1cmUgZm9yIGZhY2lhbCBlbW90aW9uIHJlY29nbml0aW9uLgoKICArIEluIHRoZSBmaXJzdCBjb2x1bW4sIDc4IGZpZHVjaWFscyBwb2ludHMgb2YgZWFjaCBlbW90aW9uIGFyZSBtYXJrZWQgaW4gb3JkZXIuCiAgKyBJbiB0aGUgc2Vjb25kIGNvbHVtbiBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlIGJldHdlZW4gcmlnaHQgcHVwaWwoMSkgYW5kICByaWdodCBicm93IHBlYWsoMjEpIGFyZSBzaG93biBpbiAgaGlzdG9ncmFtcy4gRm9yIGV4YW1wbGUsIHRoZSBkaXN0YW5jZSBvZiBhbiBhbmdyeSBmYWNlIHRlbmRzIHRvIGJlIHNob3J0ZXIgdGhhbiB0aGF0IG9mIGEgc3VycHJpc2VkIGZhY2UuCiAgKyBUaGUgdGhpcmQgY29sdW1uIGlzIHRoZSBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlcyBiZXR3ZWVuIHJpZ2h0IG1vdXRoIGNvcm5lcig1MCkKYW5kIHRoZSBtaWRwb2ludCBvZiB0aGUgdXBwZXIgbGlwKDUyKS4gIEZvciBleGFtcGxlLCB0aGUgZGlzdGFuY2Ugb2YgYW4gaGFwcHkgZmFjZSB0ZW5kcyB0byBiZSBzaG9ydGVyIHRoYW4gdGhhdCBvZiBhIHNhZCBmYWNlLgoKIVtGaWd1cmUxXSguLi9maWdzL2ZlYXR1cmVfdmlzdWFsaXphdGlvbi5qcGcpCgpgZmVhdHVyZS5SYCBpcyB0aGUgd3JhcHBlciBmb3IgYWxsIGZlYXR1cmUgZW5naW5lZXJpbmcgZnVuY3Rpb25zIGFuZCBvcHRpb25zLiBUaGUgZnVuY3Rpb24gYGZlYXR1cmUoIClgIGhhdmUgb3B0aW9ucyB0aGF0IGNvcnJlc3BvbmQgdG8gZGlmZmVyZW50IHNjZW5hcmlvcyBmb3IgdGhlIHByb2plY3QgYW5kIHByb2R1Y2VzIGFuIFIgb2JqZWN0IHRoYXQgY29udGFpbnMgZmVhdHVyZXMgYW5kIHJlc3BvbnNlcyB0aGF0IGFyZSByZXF1aXJlZCBieSBhbGwgdGhlIG1vZGVscyB0aGF0IGFyZSBnb2luZyB0byBiZSBldmFsdWF0ZWQgbGF0ZXIuCgogICsgYGZlYXR1cmUuUmAKICArIElucHV0OiBsaXN0IG9mIGltYWdlcyBvciBmaWR1Y2lhbCBwb2ludAogICsgT3V0cHV0OiBhbiBSRGF0YSBmaWxlIHRoYXQgY29udGFpbnMgZXh0cmFjdGVkIGZlYXR1cmVzIGFuZCBjb3JyZXNwb25kaW5nIHJlc3BvbnNlcwoKYGBge3IgZmVhdHVyZX0Kc291cmNlKCIuLi9saWIvZmVhdHVyZS5SIikKdG1fZmVhdHVyZV90cmFpbiA8LSBOQQpnYm1fdG1fZmVhdHVyZV90cmFpbiA8LSBOQQppZihydW4uZmVhdHVyZS50cmFpbil7CiAgdG1fZmVhdHVyZV90cmFpbiA8LSBzeXN0ZW0udGltZShkYXRfdHJhaW48LWZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCx0cmFpbl9pZHgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJ1bi5wb2x5LmZlYXR1cmUsIHJ1bi5hZGQucG9seS5mZWF0dXJlKSkKICBnYm1fdG1fZmVhdHVyZV90cmFpbiA8LSBzeXN0ZW0udGltZShnYm1fZGF0X3RyYWluPC1mZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsdHJhaW5faWR4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFLCBGQUxTRSkpCiAgc2F2ZShnYm1fZGF0X3RyYWluLCBmaWxlPSIuLi9vdXRwdXQvZ2JtX2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQogIHNhdmUoZGF0X3RyYWluLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90cmFpbi5SRGF0YSIpCn1lbHNlewogICNsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKICAjbG9hZChmaWxlPSIuLi9vdXRwdXQvZ2JtX2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQp9Cgp0bV9mZWF0dXJlX3Rlc3QgPC0gTkEKZ2JtX3RtX2ZlYXR1cmVfdGVzdCA8LSBOQQppZihydW4uZmVhdHVyZS50ZXN0KXsKICB0bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUoZGF0X3Rlc3QgPC0gZmVhdHVyZShmaWR1Y2lhbF9wdF9saXN0LCB0ZXN0X2lkeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcnVuLnBvbHkuZmVhdHVyZSwgcnVuLmFkZC5wb2x5LmZlYXR1cmUpKQogIGdibV90bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUoZ2JtX2RhdF90ZXN0IDwtIGZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCwgdGVzdF9pZHgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRkFMU0UsIEZBTFNFKSkKICBzYXZlKGdibV9kYXRfdGVzdCwgZmlsZT0iLi4vb3V0cHV0L2dibV9mZWF0dXJlX3Rlc3QuUkRhdGEiKQogIHNhdmUoZGF0X3Rlc3QsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3Rlc3QuUkRhdGEiKQp9ZWxzZXsKICBsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3Rlc3QuUkRhdGEiKQogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2dibV9mZWF0dXJlX3Rlc3QuUkRhdGEiKQp9CmBgYAoKIyMjIFN0ZXAgNDogdHJhaW4gY2xhc3NpZmljYXRpb24gbW9kZWxzIHdpdGggdHJhaW5pbmcgZmVhdHVyZXMgYW5kIHJlc3BvbnNlczsgcnVuIHRlc3Qgb24gdGVzdCBpbWFnZXMKCkNhbGwgdGhlIHRyYWluIG1vZGVsIGFuZCB0ZXN0IG1vZGVsIGZyb20gbGlicmFyeS4KCmB0cmFpbi5SYCBhbmQgYHRlc3QuUmAgYXJlIHdyYXBwZXJzIGZvciBhbGwgbW9kZWwgdHJhaW5pbmcgc3RlcHMgYW5kIGNsYXNzaWZpY2F0aW9uL3ByZWRpY3Rpb24gc3RlcHMuCgorIGB0cmFpbi5SYAogICsgSW5wdXQ6IGEgZGF0YSBmcmFtZSBjb250YWluaW5nIGZlYXR1cmVzIGFuZCBsYWJlbHMgYW5kIGEgcGFyYW1ldGVyIGxpc3QuCiAgKyBPdXRwdXQ6YSB0cmFpbmVkIG1vZGVsCisgYHRlc3QuUmAKICArIElucHV0OiB0aGUgZml0dGVkIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHVzaW5nIHRyYWluaW5nIGRhdGEgYW5kIHByb2Nlc3NlZCBmZWF0dXJlcyBmcm9tIHRlc3RpbmcgaW1hZ2VzCiAgKyBJbnB1dDogYW4gUiBvYmplY3QgdGhhdCBjb250YWlucyBhIHRyYWluZWQgY2xhc3NpZmllci4KICArIE91dHB1dDogdHJhaW5pbmcgbW9kZWwgc3BlY2lmaWNhdGlvbgoKKyBJbiB0aGlzIFN0YXJ0ZXIgQ29kZSwgd2UgdXNlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCBMQVNTTyBwZW5hbHR5IHRvIGRvIGNsYXNzaWZpY2F0aW9uLgoKYGBge3IgbG9hZGxpYn0Kc291cmNlKCIuLi9saWIvdHJhaW4uUiIpCnNvdXJjZSgiLi4vbGliL3Rlc3QuUiIpCmBgYAoKIyBCYXNlbGluZSBHQk0gTW9kZWwKCiogTW9kZWwgVHJhaW5pbmcKCmBgYHtyfQppZiAocnVuLmdibS50cmFpbil7CiAgaWYgKHNhbXBsZS5yZXdlaWdodCl7CgogICAgZ2JtX2RhdF90cmFpbiRsYWJlbCA8LSBhcy5mYWN0b3IoZ2JtX2RhdF90cmFpbiRsYWJlbCkKICAgIGRhdF90cmFpbl9iYWxhbmNlZF9nYm0gPC0gU01PVEUobGFiZWwgfiAuLCBnYm1fZGF0X3RyYWluLCBwZXJjLm92ZXIgPSAxMDAsIHBlcmMudW5kZXI9MjAwKQogICAgdGFibGUoZGF0X3RyYWluX2JhbGFuY2VkX2dibSRsYWJlbCkKCiAgICBnYm1fdG1fdHJhaW4gPC0gc3lzdGVtLnRpbWUoZ2JtX3RyYWluIDwtIHRyYWluX2dibShkYXRfdHJhaW5fYmFsYW5jZWRfZ2JtLCBzPTAuMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLPUssIG49Z2JtLm51bXRyZWVzLHcgPSBOVUxMKSkKCiAgfSBlbHNlIHsKICAgIGdibV90bV90cmFpbiA8LSBzeXN0ZW0udGltZShnYm1fdHJhaW4gPC0gdHJhaW5fZ2JtKGdibV9kYXRfdHJhaW4sIHM9MC4xLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEs9Sywgbj1nYm0ubnVtdHJlZXMsdyA9IE5VTEwpKQogIH0KCiAgIyBwbG90IHRoZSBwZXJmb3JtYW5jZQogIGJlc3QuaXRlci5vb2IgPC0gZ2JtLnBlcmYoZ2JtX3RyYWluLG1ldGhvZD0iT09CIikgIyByZXR1cm5zIG91dC1vZi1iYWcgZXN0aW1hdGVkIGJlc3QgbnVtYmVyIG9mIHRyZWVzCiAgcHJpbnQoYmVzdC5pdGVyLm9vYikKICBiZXN0Lml0ZXIuY3YgPC0gZ2JtLnBlcmYoZ2JtX3RyYWluLG1ldGhvZD0iY3YiKSAjIHJldHVybnMgSy1mb2xkIGN2IGVzdGltYXRlIG9mIGJlc3QgbnVtYmVyIG9mIHRyZWVzCiAgcHJpbnQoYmVzdC5pdGVyLmN2KQoKICBzYXZlUkRTKGdibV90cmFpbiwgIi4uL291dHB1dC9nYm1fbW9kZWwucmRzIikKICBzYXZlKGdibV90bV90cmFpbiwgYmVzdC5pdGVyLmN2LCBmaWxlPSIuLi9vdXRwdXQvZ2JtX291dHB1dHMuUkRhdGEiKQp9CgpgYGAKCiogRXZhbHVhdGlvbiBvbiBUZXN0IFNldAoKYGBge3J9CmlmKHJ1bi5nYm0udGVzdCl7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvZ2JtX291dHB1dHMuUkRhdGEiKQogIGdibV90bV90ZXN0ID0gTkEKICBmZWF0dXJlX3Rlc3QgPC0gYXMubWF0cml4KGdibV9kYXRfdGVzdFssIDE6bmNvbChnYm1fZGF0X3Rlc3QpLTFdKQoKICBnYm1fdHJhaW4gPC0gcmVhZFJEUygiLi4vb3V0cHV0L2dibV9tb2RlbC5yZHMiKQogIGdibV90bV90ZXN0IDwtIHN5c3RlbS50aW1lKHByb2JfcHJlZF9iYXNlbGluZTwtdGVzdF9nYm0oZ2JtX3RyYWluLGFzLmRhdGEuZnJhbWUoZmVhdHVyZV90ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG49YmVzdC5pdGVyLmN2LHByZWQudHlwZSA9ICdyZXNwb25zZScpKQoKICBsYWJlbF9wcmVkX2Jhc2VsaW5lIDwtIGNvbG5hbWVzKHByb2JfcHJlZF9iYXNlbGluZSlbYXBwbHkocHJvYl9wcmVkX2Jhc2VsaW5lLCAxLCB3aGljaC5tYXgpXQp9CmBgYAoKKiBTaG93IEdCTSBBY2N1cmFjeSBhbmQgQVVDCgpgYGB7cn0KaWYgKHJ1bi5nYm0udGVzdCl7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvZ2JtX291dHB1dHMuUkRhdGEiKQogIGdibV9hY2N1IDwtIG1lYW4oZ2JtX2RhdF90ZXN0JGxhYmVsID09IGxhYmVsX3ByZWRfYmFzZWxpbmUpCiAgZ2JtLmF1YyA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKGxhYmVsX3ByZWRfYmFzZWxpbmUpLCBnYm1fZGF0X3Rlc3QkbGFiZWwpCiAgZ2JtX2F1YyA9IFdlaWdodGVkQVVDKGdibS5hdWMpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgZ2JtIHRyYWluaW5nIGZlYXR1cmVzPSIsIGdibV90bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgZ2JtIHRlc3RpbmcgZmVhdHVyZXM9IiwgZ2JtX3RtX2ZlYXR1cmVfdGVzdFsxXSwgInMgXG4iKQogIGNhdCgiVGhlIEFVQyBvZiBnYm0gbW9kZWwgaXMiLCBnYm1fYXVjLCAiLlxuIikKICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBHQk0gYmFzZWxpbmUgbW9kZWwgaXMiLCBnYm1fYWNjdSoxMDAsICIlLlxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIGdibSBtb2RlbD0iLCBnYm1fdG1fdHJhaW5bMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRlc3RpbmcgbW9kZWw9IiwgZ2JtX3RtX3Rlc3RbMV0sICJzIFxuIikKfQpgYGAKCgoKIyBBZHZhbmNlZCBNb2RlbDogUmFuZG9tIEZvcmVzdAoKVGhlIHNlY29uZCBhZHZhbmNlZCBtb2RlbCBpcyByYW5kb20gZm9yZXN0LiBIZXJlIHdlIHVzZSB0aGUgZGF0YXNldHMgdGhhdCBhcmUgZXh0cmFjdGVkIGJ5IG5ldyBmZWF0dXJlIGZ1bmN0aW9ucy4gV2UgdXNlZCB0d28gbW9kZWxzIHRyYWluZWQgYnkgYm90aCBpbWJhbGFuY2VkIGFuZCBiYWxhbmNlZCBkYXRhc2V0LiBXZSB1c2VkIFNNT1RFIGZ1bmN0aW9uIHRvIGJhbGFuY2UgYm90aCB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhLiBGb3IgdHVuaW5nIHRoZSBtb2RlbCwgd2Ugc2V0IG10cnkgPSAzMDgsIHRyZWUgbnVtYmVyID0gNTAwLCBhbmQgbm9kZSBzaXplID0gMTAgZm9yIHRoZSBSRiBtb2RlbCB1c2luZyBiYWxhbmNlZCBkYXRhIChTTU9URSksIGFuZCB3ZSBzZXQgbXRyeSA9IDMwOCwgdHJlZSBudW1iZXIgPSAxNTAwLCBhbmQgbm9kZSBzaXplID0gMzAgZm9yIHRoZSBSRiBtb2RlbCB1c2luZyBpbWJhbGFuY2VkIGRhdGEuIFRoZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCBpcyBzaG93biBhdCB0aGUgZW5kIG9mIHRoaXMgc2VjdGlvbi4gIAoKVGhlIHR1bmluZyBwYXJ0IGlzIGluIGEgc2VwYXJhdGUgZmlsZSBuYW1lZCAiYXBwZW5kaXhfdHVuZV9yZi5ybWQiIGluIGRvYyBmb2xkZXIuIFBsZWFzZSBmZWVsIGZyZWUgdG8gY2hlY2sgdGhhdCB0byBzZWUgdGhlIHR1bmluZyBwcm9jZXNzLiAgCgpXZSBhbHNvIHRyYWluZWQgcmFuZG9tIGZvcmVzdCBtb2RlbCB3aXRoIHRoZSBkYXRhc2V0cyB0aGF0IGFyZSBleHRyYWN0ZWQgYnkgb2xkIGZlYXR1cmUgZnVuY3Rpb25zLiBUaGF0IGlzIGluIHBhcnQgNi4gVGhhbmsgeW91IGZvciByZWFkaW5nIQoKYGBge3J9CmlmKHJ1bi5yZil7CiAgIyMgVHJhaW5pbmcgUkYKICBpZih0cmFpbi5yZil7CiAgICByZl9kYXRfdHJhaW4gPC0gZGF0X3RyYWluCiAgICByZl9kYXRfdHJhaW4kbGFiZWwgPC0gYXMuZmFjdG9yKHJmX2RhdF90cmFpbiRsYWJlbCkKICAgIGRhdF90cmFpbl9iYWxhbmNlZF9TTU9URSA8LSBTTU9URShsYWJlbCB+IC4sIHJmX2RhdF90cmFpbiwgcGVyYy5vdmVyID0gMTIwLCBwZXJjLnVuZGVyPTIwMCkKICAgICMgVHJhaW4gUkYgYnkgYmFsYW5jZWQgZGF0YQogICAgdGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZCA8LSBzeXN0ZW0udGltZSgKICAgICAgcmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfYmFsYW5jZWQgPC0gcmFuZG9tX2ZvcmVzdF90cmFpbihkYXRfdHJhaW5fYmFsYW5jZWRfU01PVEUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSAzMDgsIHRyZWVfbnVtYmVyID0gNTAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlX3NpemUgPSAxMCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkLCBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9iYWxhbmNlZC5SRGF0YSIpCiAgICBzYXZlKHRpbWUucmYudHJhaW4uZmluYWwuYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfYmFsYW5jZWQuUkRhdGEiKQogICAgIyBUcmFpbiBSRiBieSBpbWJhbGFuY2VkIGRhdGEKICAgIHRpbWUucmYudHJhaW4uZmluYWwuaW1iYWxhbmNlZCA8LSBzeXN0ZW0udGltZSgKICAgICAgcmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfaW1iYWxhbmNlZCA8LSByYW5kb21fZm9yZXN0X3RyYWluKGRhdF90cmFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gMzA4LCB0cmVlX251bWJlciA9IDE1MDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV9zaXplID0gMzApKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLmZpbmFsLmltYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX3RpbWVfaW1iYWxhbmNlZC5SRGF0YSIpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2ltYmFsYW5jZWQsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2ltYmFsYW5jZWQuUkRhdGEiKQogIH1lbHNlewogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2JhbGFuY2VkLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF90aW1lX2JhbGFuY2VkLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF90aW1lX2ltYmFsYW5jZWQuUkRhdGEiKQogICAgbG9hZCgiLi4vb3V0cHV0L3JmX3RyYWluX2ZpbmFsX2ltYmFsYW5jZWQuUkRhdGEiKQogIH0KICAjIEV2YWx1YXRpb246CiAgaWYodGVzdC5yZil7CiAgICByZl9kYXRfdGVzdCA8LSBkYXRfdGVzdAogICAgcmZfZGF0X3Rlc3QkbGFiZWwgPC0gYXMubnVtZXJpYyhyZl9kYXRfdGVzdCRsYWJlbCkKICAgIAogICAgdGltZS5yZi50ZXN0LmZpbmFsLmJhbGFuY2VkIDwtIHN5c3RlbS50aW1lKAogICAgICByZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPC0gYXMubnVtZXJpYyhhcy52ZWN0b3IocmFuZG9tX2ZvcmVzdF90ZXN0KHJhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJmX2RhdF90ZXN0KSkpKQogICAgcmZfYWNjdXJhY3lfYmFsYW5jZWQgPC0gbWVhbihyb3VuZChyZl9wcmVkaWN0ZWRfYmFsYW5jZWQgPT0gcmZfZGF0X3Rlc3QkbGFiZWwpKQogICAgdHByLmZwci5iYWxhbmNlZCA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHJmX3ByZWRpY3RlZF9iYWxhbmNlZCkscmZfZGF0X3Rlc3QkbGFiZWwpCiAgICByZl9BVUNfYmFsYW5jZWQgPC0gV2VpZ2h0ZWRBVUModHByLmZwci5iYWxhbmNlZCkKICAgIAogICAgY2F0KCJBVUMgZm9yIHR1bmVkIFJhbmRvbSBGb3Jlc3QoYmFsYW5jZWQpOiAiLCByZl9BVUNfYmFsYW5jZWQsIi5cbiIpCiAgICBjYXQoIkFjY3VyYWN5IGZvciB0dW5lZCBSYW5kb20gRm9yZXN0KGJhbGFuY2VkKSIsIHJmX2FjY3VyYWN5X2JhbGFuY2VkKjEwMCwiJS5cbiIpCiAgICBjYXQoIlRyYWluaW5nIHRpbWUgZm9yIHR1bmVkIFJhbmRvbSBGb3Jlc3Q6ICIsIHRpbWUucmYudHJhaW4uZmluYWwuYmFsYW5jZWRbMV0sICJzLlxuIikKICAgIGNhdCgiVGVzdGluZyB0aW1lIGZvciB0dW5lZCBSYW5kb20gRm9yZXN0OiAiLCB0aW1lLnJmLnRlc3QuZmluYWwuYmFsYW5jZWRbMV0sICJzLlxuIikKICAgIGNhdCgiICAgIiwiXG4iKQogIH0KfQpgYGAKCldlIHRoaW5rIFJGIG1vZGVsIGRvIG5vdCBuZWVkIENyb3NzLVZhbGlkYXRpb24uIEhlcmUgaXMgYSBzbmlwcGV0IGZyb20gQnJlaW1hbidzIG9mZmljaWFsIGRvY3VtZW50YXRpb246CkluIHJhbmRvbSBmb3Jlc3RzLCB0aGVyZSBpcyBubyBuZWVkIGZvciBjcm9zcy12YWxpZGF0aW9uIG9yIGEgc2VwYXJhdGUgdGVzdCBzZXQgdG8gZ2V0IGFuIHVuYmlhc2VkIGVzdGltYXRlIG9mIHRoZSB0ZXN0IHNldCBlcnJvci4gSXQgaXMgZXN0aW1hdGVkIGludGVybmFsbHksIGR1cmluZyB0aGUgcnVuLCBhcyBmb2xsb3dzOiBFYWNoIHRyZWUgaXMgY29uc3RydWN0ZWQgdXNpbmcgYSBkaWZmZXJlbnQgYm9vdHN0cmFwIHNhbXBsZSBmcm9tIHRoZSBvcmlnaW5hbCBkYXRhLiBBYm91dCBvbmUtdGhpcmQgb2YgdGhlIGNhc2VzIGFyZSBsZWZ0IG91dCBvZiB0aGUgYm9vdHN0cmFwIHNhbXBsZSBhbmQgbm90IHVzZWQgaW4gdGhlIGNvbnN0cnVjdGlvbiBvZiB0aGUga3RoIHRyZWUuIFB1dCBlYWNoIGNhc2UgbGVmdCBvdXQgaW4gdGhlIGNvbnN0cnVjdGlvbiBvZiB0aGUga3RoIHRyZWUgZG93biB0aGUga3RoIHRyZWUgdG8gZ2V0IGEgY2xhc3NpZmljYXRpb24uIEluIHRoaXMgd2F5LCBhIHRlc3Qgc2V0IGNsYXNzaWZpY2F0aW9uIGlzIG9idGFpbmVkIGZvciBlYWNoIGNhc2UgaW4gYWJvdXQgb25lLXRoaXJkIG9mIHRoZSB0cmVlcy4gQXQgdGhlIGVuZCBvZiB0aGUgcnVuLCB0YWtlIGogdG8gYmUgdGhlIGNsYXNzIHRoYXQgZ290IG1vc3Qgb2YgdGhlIHZvdGVzIGV2ZXJ5IHRpbWUgY2FzZSBuIHdhcyBvb2IuIFRoZSBwcm9wb3J0aW9uIG9mIHRpbWVzIHRoYXQgaiBpcyBub3QgZXF1YWwgdG8gdGhlIHRydWUgY2xhc3Mgb2YgbiBhdmVyYWdlZCBvdmVyIGFsbCBjYXNlcyBpcyB0aGUgb29iIGVycm9yIGVzdGltYXRlLiBUaGlzIGhhcyBwcm92ZW4gdG8gYmUgdW5iaWFzZWQgaW4gbWFueSB0ZXN0cy4KCgoKCiMgQWx0ZXJuYXRpdmUgTW9kZWwgMTogU1ZNIE1vZGVsCgoqIEJhbGFuY2UgdGhlIFRyYWluaW5nIFNldAoKSWYgdGhlIGdpdmVuIGRhdGEgc2V0IGlzIGltYmFsYW5jZWQsIHdlIGFwcGx5IFNNT1RFIHRvIHJlYmFsYW5jZSB0aGUgZGF0YSBhbmQgdXNlIGl0IHRvIHRyYWluIG91ciBzdm0gbW9kZWxzLgoKYGBge3J9CmlmKHJ1bi5zdm0pewogIHRtX3N2bV9yZWJhbGFuY2VkX3RyYWluIDwtIE5BCiAgaWYoc2FtcGxlLnJld2VpZ2h0KXsKICAgIGRhdF90cmFpbiRsYWJlbCA9IGFzLmZhY3RvcihkYXRfdHJhaW4kbGFiZWwpCiAgICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSBzeXN0ZW0udGltZShzdm1fdHJhaW5pbmdfZGF0YSA8LSBTTU9URShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4pKQogICAgc2F2ZSh0bV9zdm1fcmViYWxhbmNlZF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9yZWJhbGFuY2VkX3RyYWluLlJEYXRhIikKICB9IGVsc2UgewogICAgc3ZtX3RyYWluaW5nX2RhdGEgPC0gZGF0X3RyYWluCiAgICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSB0bV9mZWF0dXJlX3RyYWluCiAgfQp9CmBgYAoKKiBNb2RlbCBTZWxlY3Rpb24KClR1bmluZyBoeXBlci1wYXJhbWV0ZXJzIGZvciBib3RoIGxpbmVhciBhbmQgcmFkaWFsIGJhc2lzIGtlcm5lbCBhbmQgc2VsZWN0IHRoZSBrZXJuZWwgbWV0aG9kIHRoYXQgcHJvZHVjZXMgdGhlIGhpZ2hlc3QgQVVDIGFuZCBhY2N1cmFjeSBhbW9uZyB0aGUgdHdvIG1ldGhvZHMuCgpgYGB7cn0KaWYocnVuLnN2bSl7CiAgc2V0LnNlZWQoMjAyMCkKICB0bV9zdm1fbGluZWFyX21vZCA8LSBOQQogIHRtX3N2bV9yYWRpYWxfbW9kIDwtIE5BCiAgaWYobW9kZWwuc2VsZWN0aW9uKXsKICAgIHN2bV9tb2RlbF9hdWMgPC0gcmVwKE5BLCAyKQogICAgc3ZtX21vZGVsX2FjY3UgPC0gcmVwKE5BLCAyKQogICAgIyMjIGxpbmVhciBrZXJuZWwKICAgIGlmKHJ1bi5jdil7CiAgICAgIGJlc3QubGluZWFyLmNvc3QgPC0gc3ZtX2xpbmVhcl9jb3N0X3R1bmUoc3ZtX3RyYWluaW5nX2RhdGEpCiAgICAgIGNhdCgiVGhlIGJlc3QgY29zdCBmb3Igc3ZtIG1vZGVsIHdpdGggbGluZWFyIGtlcm5lbCBpczogIiwgCiAgICAgICAgICBiZXN0LmxpbmVhci5jb3N0JGJlc3QucGFyYW1ldGVycyRjb3N0KQogICAgICBzdm0ubGluZWFyLnRyYWluLnN0YXJ0ID0gcHJvYy50aW1lKCkKICAgICAgdG1fc3ZtX2xpbmVhcl9tb2QgPC0gc3lzdGVtLnRpbWUoc3ZtX2xpbmVhcl9tb2QgPC0gc3ZtX2xpbmVhcl90cmFpbihzdm1fdHJhaW5pbmdfZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wMSwgSykpCiAgICAgIHN2bS5saW5lYXIudHJhaW4uZW5kID0gcHJvYy50aW1lKCkKICAgICAgc3ZtLmxpbmVhci50bSA9IHN2bS5saW5lYXIudHJhaW4uZW5kIC0gc3ZtLmxpbmVhci50cmFpbi5zdGFydAogICAgICBzYXZlKHN2bV9saW5lYXJfbW9kLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgICBzYXZlKHN2bS5saW5lYXIudG1bWzNdXSwgZmlsZT0iLi4vb3V0cHV0L3RtX3N2bV9saW5lYXJfbW9kLlJEYXRhIikKICAgIH0KICAgIHN2bV9saW5lYXJfcHJlZCA8LSBzdm1fdGVzdChzdm1fbGluZWFyX21vZCwgc3ZtX3RyYWluaW5nX2RhdGEsIEZBTFNFKQogICAgIyBldmFsdWF0ZSBwZXJmb3JtYW5jZSBvbiBsaW5lYXIga2VybmVsCiAgICBzdm1fbW9kZWxfYWNjdVsxXSA8LSBtZWFuKHJvdW5kKHN2bV9saW5lYXJfcHJlZCA9PSBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByX2xpbmVhciA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9saW5lYXJfcHJlZCksIHN2bV90cmFpbmluZ19kYXRhJGxhYmVsKQogICAgc3ZtX21vZGVsX2F1Y1sxXSA8LSBXZWlnaHRlZEFVQyh0cHIuZnByX2xpbmVhcikKCiAgICAjIyMjIHJhZGlhbCBiYXNpcyBrZXJuZWwKICAgIGlmKHJ1bi5jdil7CiAgICAgICNiZXN0LnJhZGlhbC5jb3N0IDwtIHN2bV9yYWRpYWxfY29zdF90dW5lKHN2bV90cmFpbmluZ19kYXRhKQogICAgICBzdm0ucmJmLnRyYWluLnN0YXJ0ID0gcHJvYy50aW1lKCkKICAgICAgdG1fc3ZtX3JhZGlhbF9tb2QgPCBzeXN0ZW0udGltZShzdm1fcmFkaWFsX21vZCA8LSBzdm1fcmFkaWFsX3RyYWluKHN2bV90cmFpbmluZ19kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsIEspKQogICAgICBzdm0ucmJmLnRyYWluLmVuZCA9IHByb2MudGltZSgpCiAgICAgIHN2bS5yYmYudG0gPSBzdm0ucmJmLnRyYWluLmVuZCAtIHN2bS5yYmYudHJhaW4uc3RhcnQKICAgICAgc2F2ZShzdm1fcmFkaWFsX21vZCwgZmlsZT0iLi4vb3V0cHV0L3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgICAgc2F2ZShzdm0ucmJmLnRtW1szXV0sIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICB9CiAgICBzdm1fcmFkaWFsX3ByZWQgPC0gc3ZtX3Rlc3Qoc3ZtX3JhZGlhbF9tb2QsIHN2bV90cmFpbmluZ19kYXRhLCBGQUxTRSkKICAgICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gcmJmIGtlcm5lbAogICAgc3ZtX21vZGVsX2FjY3VbMl0gPC0gbWVhbihyb3VuZChzdm1fcmFkaWFsX3ByZWQgPT0gc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpKQogICAgdHByLmZwcl9kZWZhdWx0IDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMoc3ZtX3JhZGlhbF9wcmVkKSwgc3ZtX3RyYWluaW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fbW9kZWxfYXVjWzJdIDwtIFdlaWdodGVkQVVDKHRwci5mcHJfZGVmYXVsdCkKCiAgICAjIHRhYmxlIHRvIGRpc3BsYXkgcmVzdWx0cyBmb3IgdGhlIHR3byBrZXJuZWwgbWV0aG9kcwogICAgc3ZtX3JlcyA9IG1hdHJpeChyZXAoTkEsNiksbmNvbD0zKQogICAgc3ZtX3Jlc1ssMV0gPSBzdm1fbW9kZWxfYWNjdQogICAgc3ZtX3Jlc1ssMl0gPSBzdm1fbW9kZWxfYXVjCiAgICBzdm1fcmVzWywzXSA9IGMoc3ZtLmxpbmVhci50bVszXSwgc3ZtLnJiZi50bVszXSkKICAgIGNvbG5hbWVzKHN2bV9yZXMpID0gYygiQWNjdXJhY3kiLCAiQVVDIiwiUnVubmluZyBUaW1lIikKICAgIHJvd25hbWVzKHN2bV9yZXMpID0gYygibGluZWFyIiwicmFkaWFsIGJhc2lzIikKICAgIHNhdmUoc3ZtX3JlcywgZmlsZT0iLi4vb3V0cHV0L3N2bV9tb2RlbF9zZWxlY3Rpb24uUkRhdGEiKQogIH0gZWxzZSB7CiAgICBpZighcnVuLnByZXNlbnRhdGlvbi5kYXkpewogICAgICB0bV9zdm1fcmFkaWFsX21vZCA8IHN5c3RlbS50aW1lKHN2bV9yYWRpYWxfbW9kIDwtIHN2bV9yYWRpYWxfdHJhaW4oc3ZtX3RyYWluaW5nX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwgSykpCiAgICB9IGVsc2UgewogICAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3N2bV9tb2RlbF9zZWxlY3Rpb24uUkRhdGEiKQogICAgICBzdm1fcmVzCiAgICB9CiAgfQp9CmBgYAoKClNpbmNlIHJhZGlhbCBiYXNpcyBrZXJuZWwgaGFzIGhpZ2hlciBhY2N1cmFjeSBhbmQgQVVDIHRoYW4gbGluZWFyIGtlcm5lbCwgd2Ugd2lsbCBjaG9vc2UgcmFkaWFsIGJhc2lzIGFzIG91ciBrZXJuZWwgbWV0aG9kIGZvciB0cmFpbmluZyB0aGUgc3ZtIG1vZGVsLgoKICAqIEV2YWx1YXRpb24gb24gVGVzdGluZyBEYXRhCgpgYGB7cn0KaWYocnVuLnN2bSl7CiAgc3ZtX3Rlc3RpbmdfZGF0YSA8LSBkYXRfdGVzdAogIGlmKHJ1bi5zdm0udGVzdCl7CiAgICAjIyByYmYKICAgIHRtX3N2bV9yYmZfdGVzdCA8LSBzeXN0ZW0udGltZShzdm1fcmJmX3ByZWQgPC0gc3ZtX3Rlc3Qoc3ZtX3JhZGlhbF9tb2QsIHN2bV90ZXN0aW5nX2RhdGEsIEZBTFNFKSkKICAgIHN2bV90ZXN0X2FjY3UgPSBtZWFuKHJvdW5kKHN2bV9yYmZfcHJlZCA9PSBzdm1fdGVzdGluZ19kYXRhJGxhYmVsKSkKICAgIHRwci5mcHIucmJmIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMoc3ZtX3JiZl9wcmVkKSwgc3ZtX3Rlc3RpbmdfZGF0YSRsYWJlbCkKICAgIHN2bV90ZXN0X2F1YyA9IFdlaWdodGVkQVVDKHRwci5mcHIucmJmKQogICAgc2F2ZSh0bV9zdm1fcmJmX3Rlc3QsIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmJmX3Rlc3QuUkRhdGEiKQoKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIHN2bSBtb2RlbCBpcyIsIHN2bV90ZXN0X2FjY3UqMTAwLCAiJS5cbiIpCiAgICBjYXQoIlRoZSBBVUMgb2Ygc3ZtIG1vZGVsIGlzIiwgc3ZtX3Rlc3RfYXVjLCAiLlxuIikKICB9Cn0KYGBgCgoKKiBTdW1tYXJpemUgUnVubmluZyBUaW1lCgpgYGB7cn0KaWYocnVuLnN2bSl7CiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3QgdHJhaW5pbmcgZmVhdHVyZXMgPSIsIHRtX2ZlYXR1cmVfdHJhaW5bMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdCB0ZXN0aW5nIGZlYXR1cmVzID0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHN2bSBtb2RlbCA9Iiwgc3ZtLnJiZi50bVtbM11dLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciB0ZXN0aW5nIHN2bSBtb2RlbD0iLCB0bV9zdm1fcmJmX3Rlc3RbMV0sICJzIFxuIikKfQpgYGAKCgoKIyBBbHRlcm5hdGl2ZSBNb2RlbCAyOiBSaWRnZSBNb2RlbAoKKiBBcHBseSBDb25zdHJ1Y3RlZCBSaWRnZSBNb2RlbCB0byB0aGUgVHJhaW5pbmcgU2V0CgpgYGB7cn0KaWYocnVuLnJpZGdlKXsKICB0bV9yaWRnZV90cmFpbiA8LSBOQQogIGlmICh0cmFpbi5yaWRnZSl7CiAgICBkYXRfdHJhaW5fcmViYWxhbmNlZCA8LSBST1NFKGxhYmVsIH4gLiwgZGF0YSA9IGRhdF90cmFpbiwgc2VlZD0yMDIwKSRkYXRhCiAgICB0bV9yaWRnZV90cmFpbiA8LSBzeXN0ZW0udGltZShyaWRnZV9jdl9tb2RlbDwtcmlkZ2VfdHJhaW4odHJhaW5fZGF0YT1kYXRfdHJhaW5fcmViYWxhbmNlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGE9YWxwaGEsIEs9SywgbGFtYmRhPWxhbWJkYSkpCiAgICBzYXZlKHJpZGdlX2N2X21vZGVsLCBmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQogICAgc2F2ZSh0bV9yaWRnZV90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L3JpZGdlX3RyYWluX3RpbWUuUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yaWRnZV90cmFpbl90aW1lLlJEYXRhIikKICB9Cn0KYGBgCgoqIFVzZSBDcm9zcy1WYWxpZGF0aW9uIHRvIENob29zZSB0aGUgT3B0aW1hbCBMYW1iZGEgd2l0aCBTbWFsbGVzdCBNU0UKCmBgYHtyfQppZihydW4ucmlkZ2UpewogIGlmIChydW4uY3YpewogICAgc2V0LnNlZWQoMjAyMCkKICAgIGRhdF90cmFpbl9yZWJhbGFuY2VkIDwtIFJPU0UobGFiZWwgfiAuLCBkYXRhID0gZGF0X3RyYWluLCBzZWVkPTIwMjApJGRhdGEKICAgIGZlYXR1cmVfdHJhaW4gPSBhcy5tYXRyaXgoZGF0X3RyYWluX3JlYmFsYW5jZWRbLC1kaW0oZGF0X3RyYWluX3JlYmFsYW5jZWQpWzJdXSkKICAgIGxhYmVsX3RyYWluID0gYXMuaW50ZWdlcihkYXRfdHJhaW5fcmViYWxhbmNlZCRsYWJlbCkKICAgIHJpZGdlX21vZGVsID0gY3YuZ2xtbmV0KHg9ZmVhdHVyZV90cmFpbiwgeT1sYWJlbF90cmFpbiwgYWxwaGE9YWxwaGEsIG5mb2xkcz1LLCBsYW1iZGE9bGFtYmRhKQogICAgb3B0X2xhbWJkYSA9IHJpZGdlX21vZGVsJGxhbWJkYS5taW4KICAgIHNhdmUob3B0X2xhbWJkYSwgZmlsZT0iLi4vb3V0cHV0L3JpZGdlX29wdGltYWxfbGFtYmRhLlJEYXRhIikKICB9IGVsc2UgewogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2Vfb3B0aW1hbF9sYW1iZGEuUkRhdGEiKQogIH0KfQpgYGAKCiogUHJlZGljdCBvbiBUZXN0aW5nIFNldCB3aXRoIHRoZSBPcHRpbWFsIExhbWJkYQoKYGBge3J9CmlmKHJ1bi5yaWRnZSl7CiAgdG1fcmlkZ2VfdGVzdCA9IE5BCiAgaWYocnVuLnRlc3QpewogICAgbG9hZCgiLi4vb3V0cHV0L3JpZGdlX2N2X21vZGVsLlJEYXRhIikKICAgIGRhdF90ZXN0X3JlYmFsYW5jZWQgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdGVzdCwgc2VlZD0yMDIwKSRkYXRhCiAgICBmZWF0dXJlX3Rlc3QgPC0gYXMubWF0cml4KGRhdF90ZXN0X3JlYmFsYW5jZWRbLCAtZGltKGRhdF90ZXN0X3JlYmFsYW5jZWQpWzJdXSkKICAgIHRtX3JpZGdlX3Rlc3QgPC0gc3lzdGVtLnRpbWUobGFiZWxfcHJlZDwtYXMuaW50ZWdlcihyaWRnZV90ZXN0KG1vZGVsPXJpZGdlX2N2X21vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzPWZlYXR1cmVfdGVzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVkLnR5cGUgPSAnY2xhc3MnKSkpCiAgICBzYXZlKHRtX3JpZGdlX3Rlc3QsIGZpbGU9Ii4uL291dHB1dC9yaWRnZV90ZXN0X3RpbWUuUkRhdGEiKQogIH0gZWxzZXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX3Rlc3RfdGltZS5SRGF0YSIpCiAgfQp9CmBgYAoKKiBTdW1tYXJpemUgUnVubmluZyBUaW1lCgpgYGB7cn0KaWYocnVuLnJpZGdlKXsKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0cmFpbmluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdGVzdGluZyBmZWF0dXJlcz0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHJpZGdlIG1vZGVsPSIsIHRtX3JpZGdlX3RyYWluWzFdLCAicyBcbiIpCiAgY2F0KCJUaW1lIGZvciB0ZXN0aW5nIHJpZGdlIG1vZGVsPSIsIHRtX3JpZGdlX3Rlc3RbMV0sICJzIFxuIikKfQpgYGAKCiogRXZhbHVhdGlvbiBvbiBJbmRlcGVuZGVudCBUZXN0aW5nIERhdGEKCmBgYHtyfQppZihydW4ucmlkZ2UpewogIGxvYWQoIi4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgZGF0X3Rlc3RfcmViYWxhbmNlZCA8LSBST1NFKGxhYmVsIH4gLiwgZGF0YSA9IGRhdF90ZXN0LCBzZWVkPTIwMjApJGRhdGEKICBmZWF0dXJlX3Rlc3QgPC0gYXMubWF0cml4KGRhdF90ZXN0X3JlYmFsYW5jZWRbLCAtZGltKGRhdF90ZXN0X3JlYmFsYW5jZWQpWzJdXSkKICBsYWJlbF9wcmVkID0gYXMuaW50ZWdlcihwcmVkaWN0KHJpZGdlX2N2X21vZGVsLCBzPW9wdF9sYW1iZGEsIG5ld3g9ZmVhdHVyZV90ZXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9J2NsYXNzJykpCiAgbGFiZWxfdGVzdCA9IGFzLmludGVnZXIoZGF0X3Rlc3RfcmViYWxhbmNlZCRsYWJlbCkKICByaWRnZV9hY2N1cmFjeSA9IG1lYW4ocm91bmQobGFiZWxfdGVzdD09IGxhYmVsX3ByZWQpKQogIGNhdCgiVGhlIGFjY3VyYWN5IG9mIHRoZSByaWRnZSBtb2RlbCBpcyIsIHJpZGdlX2FjY3VyYWN5KjEwMCwgIiUuXG4iKQogIHJpZGdlX0FVQyA9IGF1Yyhyb2MobGFiZWxfcHJlZCxsYWJlbF90ZXN0KSkKICBjYXQoIlRoZSBBVUMgb2YgdGhlIHJpZGdlIG1vZGVsIGlzIiwgcmlkZ2VfQVVDLCAiLlxuIikKfQpgYGAKCgoKIyBBbHRlcm5hdGl2ZSBNb2RlbCAzOiBQQ0EgKyBMREEKCiogUmViYWxhbmNlIFRyYWluaW5nIFNldAoKYGBge3J9CmlmKHJ1bi5wY2FfbGRhKXsKICBpZihzYW1wbGUucmV3ZWlnaHQpewogICAgZGF0X3RyYWluJGxhYmVsIDwtIGFzLmZhY3RvcihkYXRfdHJhaW4kbGFiZWwpCiAgICBiYWxhbmNlZF90cmFpbl9kYXRhIDwtIFNNT1RFKGxhYmVsfi4sZGF0YSA9IGRhdF90cmFpbikKICAgIHNhdmUoYmFsYW5jZWRfdHJhaW5fZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfYmFsYW5jZWRfdHJhaW4uUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGJhbGFuY2VkX3RyYWluX2RhdGEsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKICB9Cn0KYGBgCgoqIFBlcmZvcm0gUENBIGZvciBEaW1lbnNpb24gUmVkdWN0aW9uCgoqKlNpbmNlIHRoZXJlIGFyZSBvdmVyIDYwMDAgZmVhdHVyZXMsIHdlIGltcGxlbWVudCB0aGUgUENBIG1ldGhvZCB0byByZWR1Y2UgZGltZW5zaW9uIGFjY29yZGluZyB0byB0aGUgY292YXJpYW5jZSBtYXRyaXguIFdlIG9ubHkgcmV0YWluIFBDcyB3aXRoIGxhcmdlIHZhcmlhbmNlLioqCgpgYGB7ciBwY2EgbGRhfQppZihydW4ucGNhX2xkYSl7CiAgYmFsYW5jZWRfdGVzdF9kYXRhIDwtIGRhdF90ZXN0CiAgaWYocnVuLnNlbGVjdF9QQyl7CiAgICAjc2VwYXJhdGUgdGhlIGZlYXR1cmVzIGZyb20gbGFiZWwKICAgIGRhdF90cmFpbl9uZXcgPC0gYmFsYW5jZWRfdHJhaW5fZGF0YVssLWRpbShiYWxhbmNlZF90cmFpbl9kYXRhKVsyXV0KICAgIGRhdF90ZXN0X25ldyA8LSBiYWxhbmNlZF90ZXN0X2RhdGFbLC1kaW0oYmFsYW5jZWRfdGVzdF9kYXRhKVsyXV0KICAgICNjcmVhdGUgYSB2ZWN0b3IgY29udGFpbiB0YXJnZXQgbnVtYmVyIG9mIFBDcwogICAgbnVtLnBjYSA8LSBjKDEwLDUwLDUwMCwxMDAwKQogICAgdHJhaW5fcGNhIDwtIGZ1bmN0aW9uKG51bS5wY2EpewogICAgICBmb3IoaSBpbiAxOmxlbmd0aChudW0ucGNhKSl7CiAgICAgICAgI3N0YXJ0IHRpbWUgZm9yIHRyYWluaW5nIHRoZSBtb2RlbAogICAgICAgIHRyYWluLm1vZGVsLnN0YXJ0ID0gcHJvYy50aW1lKCkKICAgICAgICAjcnVuIFBDQQogICAgICAgIHBjYSA8LSBwcmNvbXAoZGF0X3RyYWluX25ldykKICAgICAgICAjc3RvcmUgZm9yIGVhY2ggcG90ZW50aWFsIFBDCiAgICAgICAgdHJhaW5fcGNhIDwtIGRhdGEuZnJhbWUocGNhJHhbLDE6bnVtLnBjYVtpXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gYmFsYW5jZWRfdHJhaW5fZGF0YVtkaW0oYmFsYW5jZWRfdHJhaW5fZGF0YSlbMl1dKQogICAgICAgIHByZWRfcGNhIDwtIHByZWRpY3QocGNhLGRhdF90ZXN0X25ldykKICAgICAgICB0ZXN0X3BjYSA8LSBkYXRhLmZyYW1lKHByZWRfcGNhWywxOm51bS5wY2FbaV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gYmFsYW5jZWRfdGVzdF9kYXRhW2RpbShiYWxhbmNlZF90ZXN0X2RhdGEpWzJdXSkKICAgICAgICAjZml0dGluZyB0aGUgbGRhIG1vZGVsCiAgICAgICAgbGRhX3BjYSA8LSBsZGEobGFiZWwgfiAuLCBkYXRhID0gdHJhaW5fcGNhKQogICAgICAgICNzdG9wIHRpbWUgZm9yIHRyYWluaW5nIHRoZSBtb2RlbAogICAgICAgIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCiAgICAgICAgI3N0YXJ0IHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgICAgICAgI3ByZWRpY3QgbGRhIG1vZGVsCiAgICAgICAgbGRhX3ByZWRfcGNhID0gcHJlZGljdChsZGFfcGNhLHRlc3RfcGNhWy1kaW0odGVzdF9wY2EpWzJdXSkKICAgICAgICAjZW5kIHRpbWUgZm9yIHRlc3RpbmcgdGhlIG1vZGVsCiAgICAgICAgdGVzdC5tb2RlbC5lbmQgPSBwcm9jLnRpbWUoKQogICAgICAgICN0ZXN0IGFjY3VyYWN5CiAgICAgICAgdGVzdF9hY2N1cmFjeT1jb25mdXNpb25NYXRyaXgobGRhX3ByZWRfcGNhJGNsYXNzLCB0ZXN0X3BjYSRsYWJlbCkkb3ZlcmFsbFsxXQogICAgICAgIHByaW50KGxpc3QobDE9dHJhaW4ubW9kZWwuZW5kIC0gdHJhaW4ubW9kZWwuc3RhcnQsCiAgICAgICAgICAgICAgICAgICBsMj10ZXN0Lm1vZGVsLmVuZCAtIHRlc3QubW9kZWwuc3RhcnQsCiAgICAgICAgICAgICAgICAgICBsMz10ZXN0X2FjY3VyYWN5KSkKICAgICAgfQogICAgfQogICAgdHJhaW5fcGNhKG51bS5wY2EpCiAgfQp9CmBgYAoKKipCeSBjb21wYXJpbmcgdGhlIHRyYWluaW5nIHRpbWUsIHRlc3QgdGltZSBhbmQgYWNjdXJhY3ksIHdlIHVzZSBtb2RlbCB3aXRoIDUwMCBQQ3MuKioKCiAgKiBNb2RlbCBUcmFpbmluZwoKYGBge3IgZmluYWwgbW9kZWx9CmlmKHJ1bi5wY2FfbGRhKXsKICB0cmFpbi5tb2RlbC5zdGFydCA9IHByb2MudGltZSgpCiAgaWYocnVuLmxkYS50cmFpbil7CiAgICBwY2FfNTAwIDwtIHByY29tcChiYWxhbmNlZF90cmFpbl9kYXRhWywtZGltKGJhbGFuY2VkX3RyYWluX2RhdGEpWzJdXSkKICAgIHRyYWluX3BjYV81MDAgPC0gZGF0YS5mcmFtZShwY2FfNTAwJHhbLDE6NTAwXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBiYWxhbmNlZF90cmFpbl9kYXRhW2RpbShiYWxhbmNlZF90cmFpbl9kYXRhKVsyXV0pCiAgICBwcmVkX3BjYV81MDAgPC0gcHJlZGljdChwY2FfNTAwLGJhbGFuY2VkX3Rlc3RfZGF0YVssLWRpbShiYWxhbmNlZF90ZXN0X2RhdGEpWzJdXSkKICAgIHRlc3RfcGNhXzUwMCA8LSBkYXRhLmZyYW1lKHByZWRfcGNhXzUwMFssMTo1MDBdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gYmFsYW5jZWRfdGVzdF9kYXRhW2RpbShiYWxhbmNlZF90ZXN0X2RhdGEpWzJdXSkKICAgIHNhdmUodHJhaW5fcGNhXzUwMCwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfcGNhX3RyYWluLlJEYXRhIikKICAgIHNhdmUodGVzdF9wY2FfNTAwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9IGVsc2UgewogICAgbG9hZCh0cmFpbl9wY2FfNTAsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3BjYV90cmFpbi5SRGF0YSIpCiAgICBsb2FkKHRlc3RfcGNhXzUwLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9wY2FfdGVzdC5SRGF0YSIpICAKICB9CiAgI2NhbGN1bGF0ZSB0aGUgdHJhaW5pbmcgdGltZQogIGxkYV9wY2FfNTAgPC0gbGRhKGxhYmVsIH4gLiwgZGF0YSA9IHRyYWluX3BjYV81MCwgY3YgPSBUUlVFKQogIHRyYWluLm1vZGVsLmVuZCA9IHByb2MudGltZSgpCn0KYGBgCgoqIENhbGN1bGF0ZSB0aGUgVHJhaW5pbmcgYW5kIFRlc3RpbmcgQWNjdXJhY3kgb2YgTERBIE1vZGVsCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIHRlc3QubW9kZWwuc3RhcnQgPSBwcm9jLnRpbWUoKQogIHByZWRfdHJhaW5fbGRhIDwtIHByZWRpY3QobGRhX3BjYV81MDAsIHRyYWluX3BjYV81MDBbLWRpbSh0cmFpbl9wY2FfNTAwKVsyXV0pCiAgYWNjdV90cmFpbl9sZGEgPC0gbWVhbihwcmVkX3RyYWluX2xkYSRjbGFzcyA9PSB0cmFpbl9wY2FfNTAwJGxhYmVsKQogIGNhdCgiVGhlIHRyYWluaWcgYWNjdXJhY3kgb2YgbW9kZWw6IExEQSIsICJpcyIsIGFjY3VfdHJhaW5fbGRhKjEwMCwgIiUuXG4iKQogICNjYWxjdWxhdGluZyB0aGUgdGVzdCB0aW1lCiAgaWYocnVuLnRlc3QpewogICAgcHJlZF90ZXN0X2xkYSA8LSBwcmVkaWN0KGxkYV9wY2FfNTAwLCB0ZXN0X3BjYV81MDApCiAgfQogIHRlc3QubW9kZWwuZW5kID0gcHJvYy50aW1lKCkKICBzYXZlKHByZWRfdGVzdF9sZGEsIGZpbGU9Ii4uL291dHB1dC9maXRfdHJhaW4uUkRhdGEiKQogIGFjY3VfdGVzdF9sZGEgPC0gbWVhbihwcmVkX3Rlc3RfbGRhJGNsYXNzID09IHRlc3RfcGNhXzUwMCRsYWJlbCkKICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBtb2RlbDogTERBIiwgImlzIiwgYWNjdV90ZXN0X2xkYSoxMDAsICIlLlxuIikKICB0cHIuZnByIDwtIFdlaWdodGVkUk9DKGFzLm51bWVyaWMocHJlZF90ZXN0X2xkYSRjbGFzcyksIHRlc3RfcGNhXzUwMCRsYWJlbCkKICBsZGFfYXVjID0gV2VpZ2h0ZWRBVUModHByLmZwcikKICBjYXQoIlRoZSBBVUMgb2YgbW9kZWw6IExEQSBpcyIsIGxkYV9hdWMsICIuXG4iKQp9CmBgYAoKKiBTdW1tYXJpemUgUnVubmluZyBUaW1lCgpQcmVkaWN0aW9uIHBlcmZvcm1hbmNlIG1hdHRlcnMsIHNvIGRvZXMgdGhlIHJ1bm5pbmcgdGltZXMgZm9yIGNvbnN0cnVjdGluZyBmZWF0dXJlcyBhbmQgZm9yIHRyYWluaW5nIHRoZSBtb2RlbCwgZXNwZWNpYWxseSB3aGVuIHRoZSBjb21wdXRhdGlvbiByZXNvdXJjZSBpcyBsaW1pdGVkLgoKYGBge3J9CmlmKHJ1bi5wY2FfbGRhKXsKICB0bV90cmFpbiA8LSB0cmFpbi5tb2RlbC5lbmQgLSB0cmFpbi5tb2RlbC5zdGFydAogIHRtX3Rlc3QgPC0gdGVzdC5tb2RlbC5lbmQgLSB0ZXN0Lm1vZGVsLnN0YXJ0CiAgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdHJhaW5pbmcgZmVhdHVyZXMgPSIsIHRtX2ZlYXR1cmVfdHJhaW5bMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0ZXN0aW5nIGZlYXR1cmVzID0iLCB0bV9mZWF0dXJlX3Rlc3RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIG1vZGVsID0iLCB0bV90cmFpblsxXSwgInMgXG4iKQogIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbCA9IiwgdG1fdGVzdFsxXSwgInMgXG4iKQp9CmBgYAoKCgojICBBbHRlcm5hdGl2ZSBNb2RlbCA0OiBSYW5kb20gRm9yZXN0IChSRikgTW9kZWwgd2l0aCBvbGQgZmVhdHVyZXM6IAoKVGhlIGZvdXJ0aCBhbHRlcm5hdGl2ZSBtb2RlbCBpcyByYW5kb20gZm9yZXN0IHVzaW5nIG9sZCBmZWF0dXJlcyBnaXZlbiBpbiB0aGUgc3RhcnRlciBjb2RlLiBIZXJlIHdlIHVzZSB0aGUgZGF0YXNldHMgdGhhdCBhcmUgZXh0cmFjdGVkIGJ5IG9sZCBmZWF0dXJlIGZ1bmN0aW9ucy4gV2UgdXNlZCB0d28gbW9kZWxzIHRyYWluZWQgYnkgYm90aCBpbWJhbGFuY2VkIGFuZCBiYWxhbmNlZCBkYXRhc2V0LiBXZSB1c2VkIFJPU0UgZnVuY3Rpb24gdG8gYmFsYW5jZSBib3RoIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEuIEZvciB0dW5pbmcgdGhlIG1vZGVsLCB3ZSBzZXQgbXRyeSA9IDMwOCwgdHJlZSBudW1iZXIgPSA1MDAsIGFuZCBub2RlIHNpemUgPSAxMCBmb3IgdGhlIFJGIG1vZGVsIHVzaW5nIGJhbGFuY2VkIGRhdGEsIGFuZCB3ZSBzZXQgbXRyeSA9IDMwOCwgdHJlZSBudW1iZXIgPSAxNTAwLCBhbmQgbm9kZSBzaXplID0gMzAgZm9yIHRoZSBSRiBtb2RlbCB1c2luZyBpbWJhbGFuY2VkIGRhdGEuIFRoZSBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCBpcyBzaG93biBhdCB0aGUgZW5kIG9mIHRoaXMgc2VjdGlvbiwgYW5kIHdlIHdpbGwgY29tcGFyZSB0aGlzIG1vZGVsIHdpdGggdGhlIFJGIG1vZGVsIHRyYWluZWQgYnkgbmV3IGZlYXR1cmVzLgoKYGBge3J9CmlmKHJ1bi5yZi5vbGQuZmVhdHVyZSl7CiAgb2xkX2ZlYXR1cmUgPC0gZnVuY3Rpb24oaW5wdXRfbGlzdCA9IGZpZHVjaWFsX3B0X2xpc3QsIGluZGV4KXsKICAgIG9sZF9wYWlyd2lzZV9kaXN0IDwtIGZ1bmN0aW9uKHZlYyl7CiAgICAgIHJldHVybihhcy52ZWN0b3IoZGlzdCh2ZWMpKSkKICAgIH0KICAgIG9sZF9wYWlyd2lzZV9kaXN0X3Jlc3VsdCA8LWZ1bmN0aW9uKG1hdCl7CiAgICAgIHJldHVybihhcy52ZWN0b3IoYXBwbHkobWF0LCAyLCBvbGRfcGFpcndpc2VfZGlzdCkpKQogICAgfQogICAgb2xkX3BhaXJ3aXNlX2Rpc3RfZmVhdHVyZSA8LSB0KHNhcHBseShpbnB1dF9saXN0W2luZGV4XSwgb2xkX3BhaXJ3aXNlX2Rpc3RfcmVzdWx0KSkKICAgIGRpbShvbGRfcGFpcndpc2VfZGlzdF9mZWF0dXJlKQogICAgb2xkX3BhaXJ3aXNlX2RhdGEgPC0gY2JpbmQob2xkX3BhaXJ3aXNlX2Rpc3RfZmVhdHVyZSwgaW5mbyRsYWJlbFtpbmRleF0pCiAgICBjb2xuYW1lcyhvbGRfcGFpcndpc2VfZGF0YSkgPC0gYyhwYXN0ZSgiZmVhdHVyZSIsIDE6KG5jb2wob2xkX3BhaXJ3aXNlX2RhdGEpLTEpLCBzZXAgPSAiIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhYmVsIikKICAgIG9sZF9wYWlyd2lzZV9kYXRhIDwtIGFzLmRhdGEuZnJhbWUob2xkX3BhaXJ3aXNlX2RhdGEpCiAgICBvbGRfcGFpcndpc2VfZGF0YSRsYWJlbCA8LSBhcy5mYWN0b3Iob2xkX3BhaXJ3aXNlX2RhdGEkbGFiZWwpCiAgICByZXR1cm4oZmVhdHVyZV9kZiA9IG9sZF9wYWlyd2lzZV9kYXRhKQogIH0KCiAgb2xkX3RtX2ZlYXR1cmVfdHJhaW4gPC0gTkEKICBpZihydW4uZmVhdHVyZS50cmFpbil7CiAgICBvbGRfdG1fZmVhdHVyZV90cmFpbiA8LSBzeXN0ZW0udGltZShvbGRfZGF0X3RyYWluIDwtIG9sZF9mZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsIHRyYWluX2lkeCkpCiAgICBzYXZlKG9sZF9kYXRfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluX29sZC5SRGF0YSIpCiAgfWVsc2V7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluX29sZC5SRGF0YSIpCiAgfQogIG9sZF90bV9mZWF0dXJlX3Rlc3QgPC0gTkEKICBpZihydW4uZmVhdHVyZS50ZXN0KXsKICAgIG9sZF90bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUob2xkX2RhdF90ZXN0IDwtIG9sZF9mZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsIHRlc3RfaWR4KSkKICAgIHNhdmUob2xkX2RhdF90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90ZXN0X29sZC5SRGF0YSIpCiAgfWVsc2V7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3Rlc3Rfb2xkLlJEYXRhIikKICB9CiAgIyB0cmFuc2ZlciBsYWJlbCBjb2x1bW4gZnJvbSBmYWN0b3IgdG8gbnVtZXJpYwogIG9sZF9kYXRfdHJhaW4kbGFiZWwgPC0gYXMubnVtZXJpYyhvbGRfZGF0X3RyYWluJGxhYmVsKQogIG9sZF9kYXRfdGVzdCRsYWJlbCA8LSBhcy5udW1lcmljKG9sZF9kYXRfdGVzdCRsYWJlbCkKICAKICAjIEJhbGFuY2UgZGF0YQogIGdibV9kYXRfdHJhaW4kbGFiZWwgPC0gYXMuZmFjdG9yKGdibV9kYXRfdHJhaW4kbGFiZWwpCiAgZGF0X3RyYWluX2JhbGFuY2VkX2dibSA8LSBTTU9URShsYWJlbCB+IC4sIGdibV9kYXRfdHJhaW4sIHBlcmMub3ZlciA9IDEwMCwgcGVyYy51bmRlcj0yMDApCiAgb2xkX2RhdF90cmFpbl9iYWxhbmNlZF9TTU9URSA8LSBkYXRfdHJhaW5fYmFsYW5jZWRfZ2JtCgogICMgVHJhaW4gTW9kZWwKICBpZih0cmFpbi5yZi5vbGQuZmVhdHVyZSl7CiAgICAjIEJhbGFuY2VkCiAgICBvbGRfdGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZCA8LSBzeXN0ZW0udGltZSgKICAgICAgb2xkX3JhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkIDwtIG9sZF9yYW5kb21fZm9yZXN0X3RyYWluKG9sZF9kYXRfdHJhaW5fYmFsYW5jZWRfU01PVEUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDMwOCwgdHJlZV9udW1iZXIgPSA1MDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV9zaXplID0gMTApKQogICAgc2F2ZShvbGRfcmFuZG9tX2ZvcmVzdF9maXRfZmluYWxfYmFsYW5jZWQsIAogICAgICAgICBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBzYXZlKG9sZF90aW1lLnJmLnRyYWluLmZpbmFsLmJhbGFuY2VkLCAKICAgICAgICAgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfdGltZV9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICAjIEltYmFsYW5jZWQKICAgIG9sZF90aW1lLnJmLnRyYWluLmZpbmFsLmltYmFsYW5jZWQgPC0gc3lzdGVtLnRpbWUoCiAgICAgIG9sZF9yYW5kb21fZm9yZXN0X2ZpdF9maW5hbF9pbWJhbGFuY2VkIDwtIG9sZF9yYW5kb21fZm9yZXN0X3RyYWluKG9sZF9kYXRfdHJhaW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gMzA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlZV9udW1iZXIgPSAxNTAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV9zaXplID0gMzApKQogICAgc2F2ZShvbGRfdGltZS5yZi50cmFpbi5maW5hbC5pbWJhbGFuY2VkLCAKICAgICAgICAgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfdGltZV9pbWJhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICAgIHNhdmUob2xkX3JhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2ltYmFsYW5jZWQsIAogICAgICAgICBmaWxlID0gIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9pbWJhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICB9ZWxzZXsKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfdGltZV9iYWxhbmNlZF9vbGRfZmVhdHVyZS5SRGF0YSIpCiAgICBsb2FkKCIuLi9vdXRwdXQvcmZfdHJhaW5fZmluYWxfdGltZV9pbWJhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICAgIGxvYWQoIi4uL291dHB1dC9yZl90cmFpbl9maW5hbF9pbWJhbGFuY2VkX29sZF9mZWF0dXJlLlJEYXRhIikKICB9CgoKICAjIEV2YWx1YXRlIE1vZGVsCiAgb2xkX3JmX2RhdF90ZXN0IDwtIG9sZF9kYXRfdGVzdAogIG9sZF9yZl9kYXRfdGVzdCRsYWJlbCA8LSBhcy5udW1lcmljKG9sZF9yZl9kYXRfdGVzdCRsYWJlbCkKICBpZih0ZXN0LnJmLm9sZC5mZWF0dXJlKXsKICBvbGRfdGltZS5yZi50ZXN0LmZpbmFsLmJhbGFuY2VkIDwtIHN5c3RlbS50aW1lKAogICAgcmZfcHJlZGljdGVkX2JhbGFuY2VkIDwtIGFzLm51bWVyaWMoYXMudmVjdG9yKG9sZF9yYW5kb21fZm9yZXN0X3Rlc3Qob2xkX3JhbmRvbV9mb3Jlc3RfZml0X2ZpbmFsX2JhbGFuY2VkLCBvbGRfcmZfZGF0X3Rlc3QpKSkpCiAgb2xkX3JmX2FjY3VyYWN5X2JhbGFuY2VkIDwtIG1lYW4ocm91bmQocmZfcHJlZGljdGVkX2JhbGFuY2VkID09IG9sZF9yZl9kYXRfdGVzdCRsYWJlbCkpCiAgb2xkX3Rwci5mcHIuYmFsYW5jZWQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhyZl9wcmVkaWN0ZWRfYmFsYW5jZWQpLG9sZF9yZl9kYXRfdGVzdCRsYWJlbCkKICBvbGRfcmZfQVVDX2JhbGFuY2VkIDwtIFdlaWdodGVkQVVDKG9sZF90cHIuZnByLmJhbGFuY2VkKQogIAogIGNhdCgiQVVDKGJhbGFuY2VkKSBmb3IgUmFuZG9tIEZvcmVzdCB3aXRoIG9sZCBmZWF0dXJlOiAiLCBvbGRfcmZfQVVDX2JhbGFuY2VkLCIuXG4iKQogIGNhdCgiQWNjdXJhY3koYmFsYW5jZWQpIGZvciBSYW5kb20gRm9yZXN0IHdpdGggb2xkIGZlYXR1cmUiLCBvbGRfcmZfYWNjdXJhY3lfYmFsYW5jZWQqMTAwLCIlLlxuIikKICBjYXQoIlRyYWluaW5nIHRpbWUgKGJhbGFuY2VkKSBmb3IgUmFuZG9tIEZvcmVzdCB3aXRoIG9sZCBmZWF0dXJlOiAiLCBvbGRfdGltZS5yZi50cmFpbi5maW5hbC5iYWxhbmNlZFsxXSwgInMuXG4iKQogIGNhdCgiVGVzdGluZyB0aW1lIChiYWxhbmNlZCkgZm9yIFJhbmRvbSBGb3Jlc3Qgd2l0aCBvbGQgZmVhdHVyZTogIiwgb2xkX3RpbWUucmYudGVzdC5maW5hbC5iYWxhbmNlZFsxXSwgInMuXG4iKQogIGNhdCgiICAgIiwiXG4iKQogIH0KfQpgYGAKCiMgR2VuZXJhdGUgYSBjc3Ygb24gcHJlc2VudGF0aW9uIGRheQoKYGBge3IgZ2VuZXJhdGUgY3N2fQppZiAocnVuLnByZXNlbnRhdGlvbi5kYXkpewogIGNzdmZpbGVvdXRwdXQ8LSIuLi9vdXRwdXQvbGFiZWxfcHJlZGljdGlvbi5jc3YiCiAgQWR2YW5jZWQ8LXJmX3ByZWRpY3RlZF9iYWxhbmNlZAogIEJhc2VsaW5lPC1sYWJlbF9wcmVkX2Jhc2VsaW5lCiAgSW5kZXg8LXRlc3RfaWR4CiAgY3N2ZGF0YSA8LSBkYXRhLmZyYW1lKEluZGV4LCBCYXNlbGluZSwgQWR2YW5jZWQpCgogIHdyaXRlLmNzdihjc3ZkYXRhLGNzdmZpbGVvdXRwdXQsIHJvdy5uYW1lcz1GQUxTRSxxdW90ZSA9IEZBTFNFKQp9CmBgYAoKIyMjIFJlZmVyZW5jZQotIER1LCBTLiwgVGFvLCBZLiwgJiBNYXJ0aW5leiwgQS4gTS4gKDIwMTQpLiBDb21wb3VuZCBmYWNpYWwgZXhwcmVzc2lvbnMgb2YgZW1vdGlvbi4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDExMSgxNSksIEUxNDU0LUUxNDYyLgo=